//////////////////////////////////////////////////////////////////////
//
//	Crytek CryENGINE Source code
//
//	File:CryPak.cpp
//  Description: Implementation of the Crytek package files management
//
//	History:
//	-Jan 31,2001:Created by Honich Andrey
//
//////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "CryPak.h"
#include <ILog.h>
#include <ISystem.h>
#include <IPlatformOS.h>
#include <IConsole.h>
#include <ITimer.h>
#include <IPerfHud.h>
#include <IStatoscope.h>
#include "zlib/zlib.h"				// crc32()
#include "md5.h"
#include "System.h"
#include "FileIOWrapper.h"
#include "CustomMemoryHeap.h"
#include "StreamEngine/FileReadAheadCache.h"
#include "CryArchive.h"
#include "StringUtils.h"

#if defined(PS3) || defined(LINUX)
#include "System.h"
#include <unistd.h>
#include <sys/stat.h>					// fstat, fileno
//#define fileno(X) ((X)->_Handle)
#endif




#include <IDiskProfiler.h>















#define CS_NEWLINE "\n"		// Callstacks on PC need a newline character, other platforms don't


typedef CryStackStringT<char, 1024> TPathStackString;
/////////////////////////////////////////////////////

#define EDITOR_DATA_FOLDER "editor"

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif //WIN32





extern CMTSafeHeap* g_pPakHeap;

#ifndef INVALID_FILE_ATTRIBUTES
#define INVALID_FILE_ATTRIBUTES ((unsigned int)-1)
#endif

static bool IsShippedLevel(const char* str)
{
	return false; // TODO: fix me!
}

inline bool IsModPath(const char* originalPath)
{
	// create copy of path and replace slashes
	CryPathString path = originalPath;
	path.replace('\\', '/');
	
	// check if field lies inside of mods/ folder
	CryStackStringT<char, 32> modsStr("m");
	modsStr += "o";
	modsStr += "d";
	modsStr += "s";
	modsStr += "/";
	const size_t modsStrLen = modsStr.length();

	if (strnicmp(path.c_str(), modsStr.c_str(), modsStrLen) == 0)
		return true;

	// check for custom SP/MP levels inside of gamecrysis2/levels/ folder
	CryStackStringT<char, 32> levelsStr("c");
	levelsStr += "3";
	levelsStr += "/";
	levelsStr += "l";
	levelsStr += "e";
	levelsStr += "v";
	levelsStr += "e";
	levelsStr += "l";
	levelsStr += "s";
	levelsStr += "/";
	const size_t levelsStrLen = levelsStr.length();
	const bool startsWithLevels = strnicmp(path.c_str(), levelsStr.c_str(), levelsStrLen) == 0;
	if (startsWithLevels && !IsShippedLevel(path.c_str() + levelsStrLen))
		return true;

	return false;
}

//////////////////////////////////////////////////////////////////////////
// IResourceList implementation class.
//////////////////////////////////////////////////////////////////////////
class CResourceList : public IResourceList
{
public:
	CResourceList() { m_iter = m_set.end(); };
	~CResourceList() {};

	stack_string UnifyFilename( const char *sResourceFile )
	{
		if(!sResourceFile)
			return ".";
		stack_string filename = sResourceFile;
		filename.replace( '\\','/' );
		filename.MakeLower();
		return filename;
	}

	virtual void Add( const char *sResourceFile )
	{
		stack_string filename = UnifyFilename(sResourceFile);

		if(strncmp(filename.c_str(),"editor/",7)==0)				// we don't want to track editor directory, on demand loading caused errors
			return;

		m_set.insert(filename);
	}
	virtual void Clear()
	{
		m_set.clear();
	}
	virtual bool IsExist( const char *sResourceFile )
	{
		stack_string filename = UnifyFilename(sResourceFile);
		if (m_set.find( CONST_TEMP_STRING(filename.c_str()) ) != m_set.end())
			return true;
		return false;
	}
	virtual bool Load( const char *sResourceListFilename )
	{
		Clear();
		CCryFile file;
		if (file.Open( sResourceListFilename,"rb" ))
		{
			int nLen = file.GetLength();
			_smart_ptr<IMemoryBlock> pMemBlock = gEnv->pCryPak->PoolAllocMemoryBlock(nLen+1,"ResourceList"); // Allocate 1 character more for Null termination.
			char *buf = (char*)pMemBlock->GetData();
			buf[nLen] = 0; // Force null terminate.
			file.ReadRaw( buf,nLen );

			// Parse file, every line in a file represents a resource filename.
			char seps[] = "\r\n";
			char *token = strtok( buf, seps );
			while (token != NULL)
			{
				Add(token);
				token = strtok( NULL, seps );
			}
			return true;
		}
		return false;
	}
	virtual const char* GetFirst()
	{
		m_iter = m_set.begin();
		if (m_iter != m_set.end())
			return *m_iter;
		return NULL;
	}
	virtual const char* GetNext()
	{
		if (m_iter != m_set.end())
		{
			m_iter++;
			if (m_iter != m_set.end())
				return *m_iter;
		}
		return NULL;
	}

	void GetMemoryStatistics(ICrySizer *pSizer)
	{
		int nSize = sizeof(*this);
		for (ResourceSet::const_iterator it = m_set.begin(); it != m_set.end(); ++it)
		{
			// Count size of all strings in the set.
			nSize += it->GetAllocatedMemory();
		}
		pSizer->AddObject( this, nSize );
		pSizer->AddObject( m_set );
	}

private:
	typedef std::set<string> ResourceSet;
	ResourceSet m_set;
	ResourceSet::iterator m_iter;
};

//////////////////////////////////////////////////////////////////////////
class CNextLevelResourceList : public IResourceList
{
public:
	CNextLevelResourceList() {};
	~CNextLevelResourceList() {};

	const char* UnifyFilename( const char *sResourceFile )
	{
		static char sFile[256];
		int len = min( (int)strlen(sResourceFile),(int)sizeof(sFile)-1 );
		int i;
		for (i = 0; i < len; i++)
		{
			if (sResourceFile[i] != '\\')
				sFile[i] = sResourceFile[i];
			else
				sFile[i] = '/';
		}
		sFile[i] = 0;
		strlwr(sFile);
		return sFile;
	}

	uint32 GetFilenameHash( const char *sResourceFile )
	{
		uint32 code = (uint32)crc32( 0L,(unsigned char*)sResourceFile,strlen(sResourceFile) );
		return code;
	}

	virtual void Add( const char *sResourceFile )
	{
		assert(0); // Not implemented
	}
	virtual void Clear()
	{
		stl::free_container(m_resources_crc32);
	}
	virtual bool IsExist( const char *sResourceFile )
	{
		uint32 nHash = GetFilenameHash(UnifyFilename(sResourceFile));
		if (stl::binary_find( m_resources_crc32.begin(),m_resources_crc32.end(),nHash ) != m_resources_crc32.end())
			return true;
		return false;
	}
	virtual bool Load( const char *sResourceListFilename )
	{
		bool bOk = false;
		m_resources_crc32.reserve(1000);
		CCryFile file;
		if (file.Open( sResourceListFilename,"rb" ))
		{
			int nFileLen = file.GetLength();
			char *buf = new char[nFileLen+16];
			file.ReadRaw( buf,nFileLen );
			buf[nFileLen] = '\0';

			// Parse file, every line in a file represents a resource filename.
			char seps[] = "\r\n";
			char *token = strtok( buf, seps );
			while (token != NULL)
			{
				uint32 nHash = GetFilenameHash(token);
				m_resources_crc32.push_back(nHash);
				token = strtok( NULL, seps );
			}
			delete []buf;
			bOk = true;
		}
		std::sort( m_resources_crc32.begin(),m_resources_crc32.end() );
		return bOk;
	}
	virtual const char* GetFirst()
	{
		assert(0); // Not implemented
		return NULL;
	}
	virtual const char* GetNext()
	{
		assert(0); // Not implemented
		return NULL;
	}
	void GetMemoryStatistics(ICrySizer *pSizer)
	{
		pSizer->AddObject( this, sizeof(*this) );
		pSizer->AddObject( m_resources_crc32 );
	}

private:
	std::vector<uint32> m_resources_crc32;
};

#define COLLECT_TIME_STATISTICS
//////////////////////////////////////////////////////////////////////////
// Automatically calculate time taken by file operations.
//////////////////////////////////////////////////////////////////////////
struct SAutoCollectFileAcessTime
{
	SAutoCollectFileAcessTime( CCryPak *pPak )
	{		
#ifdef COLLECT_TIME_STATISTICS
		m_pPak = pPak;
		m_fTime = m_pPak->m_pITimer->GetAsyncCurTime();
#endif
	}
	~SAutoCollectFileAcessTime()
	{
#ifdef COLLECT_TIME_STATISTICS
		m_fTime = m_pPak->m_pITimer->GetAsyncCurTime() - m_fTime;
		m_pPak->m_fFileAcessTime += m_fTime;
#endif
	}
private:
	CCryPak *m_pPak;
	float m_fTime;
};

static void fileAccessMessage(int threadIndex, const char* inName)
{
	static volatile bool s_threadAndRecursionGuard = false;

	if (s_threadAndRecursionGuard == false)
	{
		s_threadAndRecursionGuard = true;

		const char* name = strchr(inName, ':');
		name = name ? (name + 2) : inName;

		const char* threadName = (threadIndex==0) ? "main" : "render";
		CryFixedStringT<2048> msg;
		const char *funcs[32];
		int nCount = 32;

		gEnv->pSystem->debug_GetCallStack( funcs, nCount );

		msg.Format( "File opened on %s thread:\n\n%s\n\n --- Callstack ---\n", 
			threadName, name );

		CryFixedStringT<256> temp;

		for (int i = 1; i < nCount; i++)
		{
			temp.Format( "%02d) %s" CS_NEWLINE, i, funcs[i] );
			msg.append( temp );
		}
		OutputDebugString( msg );

		IPlatformOS::EMsgBoxResult result;

		IPlatformOS* pOS = gEnv->pSystem->GetPlatformOS();
		result = pOS->DebugMessageBox( msg.c_str(), "TRC/TCR Fail: Syncronous File Access" );

		if (result == IPlatformOS::eMsgBox_Cancel)
		{
			DebugBreak();
		}
		else
		{
			Sleep(33);
		}
		s_threadAndRecursionGuard = false;
	}
}

/////////////////////////////////////////////////////
// Initializes the crypak system;
//   pVarPakPriority points to the variable, which is, when set to 1,
//   signals that the files from pak should have higher priority than filesystem files
CCryPak::CCryPak(IMiniLog* pLog, PakVars* pPakVars, const bool bLvlRes ):
m_pLog (pLog),
m_eRecordFileOpenList(RFOM_Disabled),
m_pPakVars (pPakVars?pPakVars:&g_cvars.pakVars),
m_fFileAcessTime(0.f),
m_bLvlRes(bLvlRes),
m_renderThreadId(0),
m_bUseWhiteList(false),
m_bUseWhiteListCRCs(false)
{
#ifdef LINUX
	m_HandleSource = 0;
#endif
	m_pITimer = gEnv->pTimer;

	m_bGameFolderWritable = true;
	m_disableRuntimeFileAccess[0] = m_disableRuntimeFileAccess[1] = false;

	// Default game folder
	m_strDataRoot = "game";
	m_strDataRootWithSlash = m_strDataRoot + string(g_cNativeSlash);

	m_pEngineStartupResourceList = new CResourceList;
	m_pLevelResourceList = new CResourceList;
	m_pNextLevelResourceList = new CNextLevelResourceList;
	m_arrAliases.reserve(16);
	m_arrArchives.reserve(64);




	m_bInstalledToHDD = true;


	m_mainThreadId = GetCurrentThreadId();

	m_pReadAheadCache = new CFileReadAheadCache;

	gEnv->pSystem->GetISystemEventDispatcher()->RegisterListener(this);

	// Force early construction of file read sequencer here
	GetFileReadSequencer();
}

bool CCryPak::CheckFileAccessDisabled(const char* name, const char* mode)
{
#if defined(ENABLE_PROFILING_CODE)
	if (gEnv->IsEditor())
		return true;

	int logInvalidFileAccess = m_pPakVars->nLogInvalidFileAccess;
	int msgInvalidFileAccess = m_pPakVars->nMessageInvalidFileAccess;

	if( logInvalidFileAccess | msgInvalidFileAccess )
	{
		if (gEnv->pSystem->IsSerializingFile())
			return true;

		uint32 currentThreadId = GetCurrentThreadId(); // potentially expensive call
		int threadIndex = -1;

		if (currentThreadId == m_mainThreadId)
		{
			threadIndex = 0;
		}
		else if (currentThreadId == m_renderThreadId)
		{
			threadIndex = 1;
		}
		if( threadIndex >= 0 && m_disableRuntimeFileAccess[threadIndex] )
		{
			if ( msgInvalidFileAccess )
			{
				fileAccessMessage(threadIndex, name);
			}
			if ( logInvalidFileAccess )
			{
				bool oldDrfa = DisableRuntimeFileAccess( false, currentThreadId );
				CryWarning( VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "File: %s opened when runtime file access is disabled (mode %s)", name, mode );

				//strip off dir to reduce warning size
				const char* nameShort = strrchr(name, '/');

				if(!nameShort)
					nameShort = strrchr(name, '\\');

				if(nameShort)
				{
					nameShort = nameShort+1;
				}
				else
				{
					//no slashes in name
					nameShort = name;
				}
				
				if(logInvalidFileAccess==2)
				{
					LogFileAccessCallStack(nameShort, name, mode);
				}

				{
					char acTmp[2048];
					sprintf_s(acTmp, 2048,"Invalid File Access: %s '%s'", nameShort, mode);
					gEnv->pSystem->DisplayErrorMessage(acTmp, 5.f);
				}

				CryPerfHUDWarning(5.f, "File Access: %s '%s'", nameShort, mode);
				DisableRuntimeFileAccess( oldDrfa, currentThreadId );
			}
#if ENABLE_STATOSCOPE
			if (gEnv->pStatoscope)
			{
				const char *sThreadName = CryThreadGetName(currentThreadId);
				char sThreadNameBuf[11]; // 0x 12345678 \0 => 2+8+1=11
				if (!sThreadName || !sThreadName[0])
				{
					_snprintf(sThreadNameBuf, sizeof(sThreadNameBuf), "0x%08x", currentThreadId);
					sThreadName = sThreadNameBuf;
				}

				string userMarker = "file access (";
				userMarker += sThreadName;
				userMarker += ") ";
				userMarker += name;
				gEnv->pStatoscope->AddUserMarker(userMarker.c_str());
			}
#endif // ENABLE_STATOSCOPE
			return true;
		}
	}
#endif
	return false;
}

void CCryPak::LogFileAccessCallStack(const char* name, const char* nameFull, const char* mode)
{
	//static std::vector<string> s_InvalidFileNames;
	static bool s_Init=false;
	static int s_AccessCount=0;

	if(!s_Init)
	{
		MakeDir("FileAccess");
		s_Init=true;
	}

	/*
	// don't output the same file twice to reduce the amount if logging
	std::vector<string>::iterator itFindRes = std::find(s_InvalidFileNames.begin(), s_InvalidFileNames.end(), name);
	if (itFindRes != s_InvalidFileNames.end())
		return;
	s_InvalidFileNames.push_back(name); 
	*/

	string n(name);
	int idx = n.find('.');
	string filename;

	if(idx!=-1)
	{
		n.replace(idx, n.length()-idx, '\0'); //truncate extension

		filename.Format("FileAccess/%s_%s_%d.log", name+idx+1, n.c_str(), ++s_AccessCount);
	}
	else
	{
		filename.Format("FileAccess/%s_%d.log", n.c_str(), ++s_AccessCount);
	}

	char tempPath[MAX_PATH];
	const char* szPath = AdjustFileName(filename.c_str(),tempPath,FLAGS_PATH_REAL | FLAGS_FOR_WRITING);
























}

//////////////////////////////////////////////////////////////////////////
void CCryPak::SetGameFolderWritable( bool bWritable )
{
	m_bGameFolderWritable = bWritable;
}

//////////////////////////////////////////////////////////////////////////
void CCryPak::AddMod(const char* szMod)
{
	// remember the prefix to use to convert the file names
	CryPathString strPrepend = szMod;
	strPrepend.replace( g_cNativeSlash,g_cNonNativeSlash );
	strPrepend.MakeLower();

	std::vector<string>::iterator strit;
	for (strit = m_arrMods.begin(); strit != m_arrMods.end(); ++strit)
	{
		string &sMOD = *strit;
		if (stricmp(sMOD.c_str(),strPrepend.c_str())==0)
			return; // already added
	}
	m_arrMods.push_back(strPrepend);
}

//////////////////////////////////////////////////////////////////////////
void CCryPak::RemoveMod(const char* szMod)
{
	CryPathString strPrepend = szMod;
	strPrepend.replace( g_cNativeSlash,g_cNonNativeSlash );
	strPrepend.MakeLower();

	std::vector<string>::iterator it;
	for (it = m_arrMods.begin(); it != m_arrMods.end(); ++it)
	{
		string &sMOD = *it;
		if (stricmp(sMOD.c_str(),strPrepend.c_str())==0)
		{
			m_arrMods.erase(it);
			break;
		}
	} //it
}

//////////////////////////////////////////////////////////////////////////
const char* CCryPak::GetMod(int index)
{
	return index >= 0 && index < (int)m_arrMods.size() ? m_arrMods[index].c_str() : NULL;
}

//////////////////////////////////////////////////////////////////////////

bool CCryPak::IsInstalledToHDD(const char* acFilePath) const
{
	if (m_bInstalledToHDD || g_cvars.sys_force_installtohdd_mode)
		return true;

	if (acFilePath)
	{
		const ICryArchive* pArchive = FindArchive(acFilePath);
		if (pArchive && pArchive->GetFlags() & ICryArchive::FLAGS_ON_HDD)
			return true;
	}
		
	return false;
}

void CCryPak::SetInstalledToHDD(bool bValue)
{
	m_bInstalledToHDD = bValue;

	IStreamEngine* pStreamEngine = gEnv->pSystem->GetStreamEngine();
	if (m_bInstalledToHDD && pStreamEngine)
	{
		// inform streaming system that data is available on HDD
		pStreamEngine->SetStreamDataOnHDD(true);
	}
}

//////////////////////////////////////////////////////////////////////////
void CCryPak::ParseAliases(const char* szCommandLine)
{
	const char *szVal=szCommandLine;
	while (1)
	{			
		// this is a list of pairs separated by commas, i.e. Folder1,FolderNew,Textures,TestBuildTextures etc.
		const char *szSep=strstr(szVal,","); 
		if (!szSep)
			break; // bogus string passed.

		char szName[256],szAlias[256];

		const char *szSepNext=strstr(szSep+1,","); // find next pair (skip comma)		

		int nLen = (int)(szSep-szVal);
		Limit(nLen,0,255); // will never happen but just to pacify the code analysis
		strncpy(szName,szVal,nLen); // old folder name
		szName[nLen]=0; // fix the string.

		if (!szSepNext)
		{
			// the system is returning the whole command line instead of only the next token
			// check if there are other commands on the command line and skip them.
			szSepNext=strstr(szSep+1," "); 
			if (szSepNext)
			{			
				nLen = (int)((szSepNext-1)-(szSep));
				Limit(nLen,0,255); // will never happen but just to pacify the code analysis
				strncpy(szAlias,szSep+1,nLen); // skip 2 commas - copy alias name till the next trail
				szAlias[nLen]=0; // fix the string.
				szSepNext=NULL; // terminate the loop.
			}
			else
				strcpy(szAlias,szSep+1); // skip comma - copy alias name 
		}
		else
		{
			nLen = (int)((szSepNext-1)-(szSep));
			Limit(nLen,0,255); // will never happen but just to pacify the code analysis

			strncpy(szAlias,szSep+1,nLen); // skip 2 commas - copy alias name till the next trail
			szAlias[nLen]=0; // fix the string.
		}

		SetAlias(szName,szAlias,true); // inform the pak system.

		CryLogAlways("PAK ALIAS:%s,%s\n",szName,szAlias);
		if (!szSepNext)
			break; // no more aliases
		szVal=szSepNext+1; // move over to the next pair (skip comma)
	}
}

//////////////////////////////////////////////////////////////////////////
//! if bReturnSame==true, it will return the input name if an alias doesn't exist. Otherwise returns NULL
const char *CCryPak::GetAlias(const char* szName,bool bReturnSame)
{	
	const TAliasList::const_iterator cAliasEnd = m_arrAliases.end();
	for (TAliasList::const_iterator it=m_arrAliases.begin();it!=cAliasEnd;++it)
	{
		tNameAlias *tTemp=(*it);
		if (stricmp(tTemp->szName,szName)==0)		
			return (tTemp->szAlias);		
	} //it
	if (bReturnSame)
		return (szName);
	return (NULL);
}

//////////////////////////////////////////////////////////////////////////
//! Set "Game" folder (/Game, /Game04, ...)
void CCryPak::SetGameFolder(const char* szFolder)
{
	assert(szFolder);
	m_strDataRoot = GetAlias(szFolder, true);
	m_strDataRoot.MakeLower();
	m_strDataRootWithSlash = m_strDataRoot + string(g_cNativeSlash);

#ifdef WIN32
	// Check that game folder exist, produce fatal error if missing.
	{
		__finddata64_t fd;
		ZeroStruct(fd);
		intptr_t hfile = 0;
		hfile = _findfirst64(m_strDataRoot, &fd);
		_findclose(hfile);
		if (!(fd.attrib & _A_SUBDIR))
		{
			CryFatalError( "Game Folder %s not found",m_strDataRoot.c_str() );
		}
	}
#endif
}

//////////////////////////////////////////////////////////////////////////
//! Get "Game" folder (/Game, /Game04, ...)
const char* CCryPak::GetGameFolder() const
{
	return(m_strDataRoot);
}

//////////////////////////////////////////////////////////////////////////
void CCryPak::SetLocalizationFolder(char const* const sLocalizationFolder)
{
	// Get the localization folder
	m_sLocalizationFolder = sLocalizationFolder;
#if defined(PS3) || defined(LINUX) || defined(CAFE)
	m_sLocalizationFolder += "/";
#else
	m_sLocalizationFolder += "\\";
#endif
}

//////////////////////////////////////////////////////////////////////////
void CCryPak::SetAlias(const char* szName,const char* szAlias,bool bAdd)
{
	// find out if it is already there
	TAliasList::iterator it;
	tNameAlias *tPrev=NULL;
	for (it=m_arrAliases.begin();it!=m_arrAliases.end();++it)
	{
		tNameAlias *tTemp=(*it);
		if (stricmp(tTemp->szName,szName)==0)
		{
			tPrev=tTemp;
			if (!bAdd)
			{
				//remove it
				SAFE_DELETE(tPrev->szName);
				SAFE_DELETE(tPrev->szAlias);
				delete tPrev;
				m_arrAliases.erase(it);				
			}
			break;
		}
	} //it

	if (!bAdd)
		return;

	if (tPrev)
	{
		// replace existing alias
		if (stricmp(tPrev->szAlias,szAlias)!=0)
		{		
			SAFE_DELETE(tPrev->szAlias);
			tPrev->nLen2=strlen(szAlias);
			tPrev->szAlias=new char [tPrev->nLen2+1]; // includes /0
			strcpy(tPrev->szAlias,szAlias);
			// make it lowercase
			strlwr(tPrev->szAlias);
		}
	}
	else
	{
		// add a new one
		tNameAlias *tNew=new tNameAlias;

		tNew->nLen1=strlen(szName);
		tNew->szName=new char [tNew->nLen1+1]; // includes /0
		strcpy(tNew->szName,szName);
		// make it lowercase
		strlwr(tNew->szName);

		tNew->nLen2=strlen(szAlias);
		tNew->szAlias=new char [tNew->nLen2+1]; // includes /0
		strcpy(tNew->szAlias,szAlias);
		// make it lowercase
		strlwr(tNew->szAlias);

		m_arrAliases.push_back(tNew);
	}
}

//////////////////////////////////////////////////////////////////////////
CCryPak::~CCryPak()
{
	gEnv->pSystem->GetISystemEventDispatcher()->RemoveListener(this);
	
	m_arrZips.clear();

	delete m_pReadAheadCache;
	m_pReadAheadCache = 0;

	unsigned numFilesForcedToClose = 0;
	// scan through all open files and close them
	{
		AUTO_MODIFYLOCK(m_csOpenFiles);
		for (ZipPseudoFileArray::iterator itFile = m_arrOpenFiles.begin(); itFile != m_arrOpenFiles.end(); ++itFile)
		{
			if (itFile->GetFile())
			{
				itFile->Destruct();
				++numFilesForcedToClose;
			}
		}
	}

	if (numFilesForcedToClose)
		m_pLog->LogWarning ("%u files were forced to close", numFilesForcedToClose);

	{
		AUTO_MODIFYLOCK(m_csCachedFiles);
		size_t numDatasForcedToDestruct = m_setCachedFiles.size();
		for (size_t i = 0; i < numDatasForcedToDestruct; ++i)
			if (m_setCachedFiles.empty())
			{
				assert (0);
			}
			else
			{
				delete *m_setCachedFiles.begin();
			}
			if (numDatasForcedToDestruct)
				m_pLog->LogWarning ("%u cached file data blocks were forced to destruct; they still have references on them, crash possible", (unsigned int)numDatasForcedToDestruct);
	}

	if (!m_arrArchives.empty())
		m_pLog->LogError("There are %d external references to archive objects: they have dangling pointers and will either lead to memory leaks or crashes", (int)m_arrArchives.size());

	if (!m_mapMissingFiles.empty())
	{
		FILE* f = CIOWrapper::Fopen("Missing Files Report.txt", "wt");
		if (f)
		{
			AUTO_LOCK_CS(m_csMissingFiles);
			for (MissingFileMap::iterator it = m_mapMissingFiles.begin(); it != m_mapMissingFiles.end(); ++it)
				fprintf (f, "%d\t%s\n", it->second,it->first.c_str());
			CIOWrapper::Fclose(f);
		}
	}

	{
		const TAliasList::iterator cAliasEnd = m_arrAliases.end();
		for (TAliasList::iterator it=m_arrAliases.begin();it!=cAliasEnd;++it)
		{
			tNameAlias *tTemp=(*it);
			SAFE_DELETE(tTemp->szName);
			SAFE_DELETE(tTemp->szAlias);
			delete tTemp;
		}
	}
}


// makes the path lower-case and removes the duplicate and non native slashes
// may make some other fool-proof stuff
// may NOT write beyond the string buffer (may not make it longer)
// returns: the pointer to the ending terminator \0
char* CCryPak::BeautifyPath(char* dst)
{
	// make the path lower-letters and with native slashes
	char*p,*q;
	// there's a special case: two slashes at the beginning mean UNC filepath
	p = q = dst;
	if (*p == g_cNonNativeSlash || *p == g_cNativeSlash)
		++p,++q; // start normalization/beautifications from the second symbol; if it's a slash, we'll add it, too

	while (*p)
	{
		if (*p == g_cNonNativeSlash || *p == g_cNativeSlash)
		{
			*q = g_cNativeSlash;
			++p,++q;
			while(*p == g_cNonNativeSlash || *p == g_cNativeSlash)
				++p; // skip the extra slashes
		}
		else
		{
			*q = tolower (*p);
			++q,++p;
		}
	}
	*q = '\0';

 	RemoveRelativeParts(dst);
 	return q;
}

char* CCryPak::BeautifyPathForWrite(char* dst)
{
	// make the path lower-letters and with native slashes
	char*p,*q;
	// there's a special case: two slashes at the beginning mean UNC filepath
	p = q = dst;
	if (*p == g_cNonNativeSlash || *p == g_cNativeSlash)
		++p,++q; // start normalization/beautifications from the second symbol; if it's a slash, we'll add it, too

	bool bMakeLower = false;

	while (*p)
	{
		if (*p == g_cNonNativeSlash || *p == g_cNativeSlash)
		{
			*q = g_cNativeSlash;
			++p,++q;
			while(*p == g_cNonNativeSlash || *p == g_cNativeSlash)
				++p; // skip the extra slashes
		}
		else
		{
			if (*p == '%')
				bMakeLower = !bMakeLower;

			if (bMakeLower)
				*q = tolower (*p);
			else
				*q = *p;
			++q,++p;
		}
	}
	*q = '\0';
	return q;
}

// remove all '%s/..' or '.' parts from the path (needs beautified path - only single native slashes)
// e.g. Game/Scripts/AI/../Entities/foo -> Game/Scripts/Entities/foo
// e.g. Game/Scripts/./Entities/foo -> Game/Scripts/Entities/foo
void CCryPak::RemoveRelativeParts(char* dst)
{
	char* q = dst;
	char* p = NULL;
	char s[5];

	// replace all '/./' with '/'
	sprintf(s, "%c.%c", g_cNativeSlash, g_cNativeSlash);

	while (p = strstr(q, s))
		strcpy(p, p + 2);

	// replace all '/%s/../' with '/'
	sprintf(s, "%c..%c", g_cNativeSlash, g_cNativeSlash);

	while (p = strstr(q, s))
	{
		if (p != q) // only remove if not in front of a path
		{
			int i = 4;
			while (*(--p) != g_cNativeSlash && p != q)
				i++;

			strcpy(p, p + i);
		}
		else
			q += 3;
	}
}

namespace filehelpers
{
	//////////////////////////////////////////////////////////////////////////
	inline bool CheckPrefix( const char *str,const char *prefix )
	{
		//this should rather be a case insensitive check here, so strnicmp is used instead of strncmp
		return (strnicmp(str,prefix,strlen(prefix)) == 0);
	}

	//////////////////////////////////////////////////////////////////////////
	inline int64 GetFileSizeOnDisk( const char *filename )
	{



		struct stat desc;


		if (stat(filename, &desc) == 0)
		{
			return desc.st_size;
		}
		return -1;
	}

	//////////////////////////////////////////////////////////////////////////
	inline bool CheckFileExistOnDisk( const char *filename )
	{
		return GetFileSizeOnDisk(filename) >= 0;
	}

};

//////////////////////////////////////////////////////////////////////////

const char* CCryPak::AdjustFileName( const char *src, char dst[g_nMaxPath], unsigned nFlags )
{
	bool bSkipMods = false;

	if (!bSkipMods &&
		((nFlags&FLAGS_PATH_REAL) == 0) && 
		((nFlags&FLAGS_FOR_WRITING) == 0) &&
		(!m_arrMods.empty()) && 
		(*src != '%') &&                                // If path starts from % it is a special alias.
		((m_pPakVars->nPriority != ePakPriorityPakOnly) || (nFlags&FLAGS_NEVER_IN_PAK)))	// When priority is Pak only, we only check Mods directories if we're looking for a file that can't be in a pak
	{
			// Scan mod folders
			std::vector<string>::reverse_iterator it;
			for (it = m_arrMods.rbegin(); it != m_arrMods.rend(); ++it)
			{
				CryPathString modPath = (*it).c_str();
				modPath.append(1,'/');
				modPath += src;
				const char *szFinalPath = AdjustFileNameInternal( modPath,dst,nFlags|FLAGS_PATH_REAL );

				if (m_pPakVars->nPriority == ePakPriorityFileFirstModsOnly && !IsModPath(szFinalPath))
					continue;

				if( nFlags&FLAGS_NEVER_IN_PAK )
				{
					// only check the filesystem 
					if (filehelpers::CheckFileExistOnDisk(szFinalPath))
						return szFinalPath;
				}
				else
				{
					//look for regular files depending on the pak priority
					switch (m_pPakVars->nPriority)
					{
					case ePakPriorityFileFirstModsOnly:
					case ePakPriorityFileFirst:
						{
							if (filehelpers::CheckFileExistOnDisk(szFinalPath))
								return szFinalPath;
							if (FindPakFileEntry(szFinalPath))
								return szFinalPath;
						}
						break;
					case ePakPriorityPakFirst:
						{
							if (FindPakFileEntry(szFinalPath))
								return szFinalPath;
							if (filehelpers::CheckFileExistOnDisk(szFinalPath))
								return szFinalPath;
						}
						break;
					}

				}
			} //it
	}

	return AdjustFileNameInternal( src,dst,nFlags );
}
//////////////////////////////////////////////////////////////////////////
bool CCryPak::AdjustAliases(char *dst) 
{
	// since CCryPak::SetAlias happens once at the game start, we can temporary remove it

	char szDest[512];

	bool bFoundAlias = false;
	const TAliasList::const_iterator cAliasEnd = m_arrAliases.end();
	for (TAliasList::const_iterator it=m_arrAliases.begin();it!=cAliasEnd;++it)
	{
		tNameAlias *tTemp=(*it);
		// find out if the folder is used
		char *szSrc=dst;		
		do 
		{
			//if (*szSrc==g_cNativeSlash)
			//	break; // didnt find any

			const char *szComp=tTemp->szName;
			if (*szSrc==*szComp)
			{
				char *szSrc2=szSrc;
				int k;
				for (k=0;k<tTemp->nLen1;k++)
				{					
					if ( (!(*szSrc2)) || (!(*szComp)) || (*szSrc2!=*szComp))
						break;					
					szSrc2++;
					szComp++;
				}

				if (k<tTemp->nLen1)
					break; // comparison failed, stop

				// we must verify that the next character is a slash, to be sure 
				// this is the whole folder and we aren't erroneously replacing partial folders (ex. Game04 with Game1) 
				if (*szSrc2!=g_cNativeSlash)
					break; // comparison failed, stop

				// replace name
				int nLenDiff=(int)(szSrc-dst);
				memcpy(szDest,dst,nLenDiff); // copy till the name to be replaced
				memcpy(szDest+nLenDiff,tTemp->szAlias,tTemp->nLen2); // add the new name
				// add the rest
				//strcat(szDest+nLenDiff+tTemp->nLen2,dst+nLenDiff+tTemp->nLen1); 
				szSrc2=dst+nLenDiff+tTemp->nLen1;
				szSrc=szDest+nLenDiff+tTemp->nLen2;
				while (*szSrc2)
				{
					*szSrc++=*szSrc2++;
				} 				
				*szSrc=0;
				memcpy(dst,szDest,256);
				bFoundAlias = true;
				break; // done
			}

			break; // check only the first folder name, skip the rest.

		} while (*szSrc++);
	} //it

	return bFoundAlias;
}




#if defined LINUX
extern char *fopenwrapper_basedir;
#endif

void AdjustDriveForXenon(char*& dst, const char* szNewSrc, unsigned nFlags)
{
#if defined(XENON) &&(!defined (_RELEASE) || defined(ENABLE_PROFILING_CODE) || defined(XENON_ALLOW_WRITING_TO_HDD))
	if (nFlags&ICryPak::FLAGS_FOR_WRITING)
	{
		static bool bMapInitialize = true;
		static bool bIsDWritable = false;
		if (bMapInitialize) {
			bMapInitialize = false;
			DmMapDevkitDrive();
			FILE * f = fopen("d:\\test", "wb");
			if (f) {
				bIsDWritable = true;	
				fclose(f);
				DeleteFile("d:\\test");
			} else {
				CryCreateDirectory("e:\\dvd\\", NULL);
			}
		}
		if (bIsDWritable)
			strcpy(dst, "d:\\");
		else {
			strcpy(dst, "e:\\dvd\\");
		}
		strcat(dst, szNewSrc);		
	}
	else
#endif
	{
		strcpy(dst, "d:\\");
		strcat(dst, szNewSrc);
	}
}

	
void CCryPak::GetCachedPakCDROffsetSize(const char* szName,uint32 &offset,uint32 &size)
{
	offset = 0;			// play it safe
	size = 0;

#if defined(DEDICATED_SERVER)
	char szFullPathBuf[g_nMaxPath];

	const char *szFullPath = AdjustFileName(szName, szFullPathBuf, FOPEN_HINT_QUIET);

	// scan through registered pak files and try to find this file
	for (ZipArray::reverse_iterator itZip = m_arrZips.rbegin(); itZip != m_arrZips.rend(); ++itZip)
	{
		if( itZip->pArchive->GetFlags() & ICryArchive::FLAGS_DISABLE_PAK )
			continue;

		const char *pathToZip = itZip->pZip->GetFilePath();
		
		if (stricmp(pathToZip, szFullPath)==0)
		{
			itZip->pZip->GetCDROffsetSize(offset,size);
			return;					// May as well early out
		}
	}

#endif//DEDICATED_SERVER
}

//////////////////////////////////////////////////////////////////////////
// given the source relative path, constructs the full path to the file according to the flags
const char* CCryPak::AdjustFileNameInternal(const char *src, char *dst, unsigned nFlags )
{
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_SYSTEM );
	// in many cases, the path will not be long, so there's no need to allocate so much..
	// I'd use _alloca, but I don't like non-portable solutions. besides, it tends to confuse new developers. So I'm just using a big enough array
	char szNewSrc[g_nMaxPath];
	strcpy_s(szNewSrc, src);

	//for PS3: currently it gets lower cased in the WinBase abstraction as it needs a prefix anyway
	if (nFlags & FLAGS_FOR_WRITING)
	{
#if defined(WIN32)
		// Path is adjusted for writing file.
		if (!m_bGameFolderWritable)
		{
			// If game folder is not writable on Windows, we must adjust the path to go into the user folder if it is not already starts from alias or absolute.
			if (*src != '%' && src[0] != 0 && src[1] != ':')
			{
				// If game folder is not writable on Windows, we must adjust the path to go into the user folder if not already.
				strcpy_s(szNewSrc,"%user%\\");
				strcat_s(szNewSrc,src);
			}
		}
#endif


























		if ((nFlags&FLAGS_NO_LOWCASE) == 0)
		{
			BeautifyPathForWrite(szNewSrc);
		}
	}
	else
	{
		if ((nFlags&FLAGS_NO_LOWCASE) == 0)
			BeautifyPath(szNewSrc);
	}
	bool bAliasWasUsed = AdjustAliases(szNewSrc);

	if (nFlags & FLAGS_NO_FULL_PATH)
	{
		strcpy(dst,szNewSrc);
		return (dst);
	}

	bool isAbsolutePath = false;

#if !defined(PS3)//for PS3 HDD the dir cannot be absolute
	if (szNewSrc[0] == g_cNativeSlash || szNewSrc[0] == g_cNonNativeSlash
		|| szNewSrc[1] == ':')
	{
		isAbsolutePath = true;
	}
#endif	

	//////////////////////////////////////////////////////////////////////////
	// Strip ./ or .\\ at the begining of the path when absolute path is givven.
	//////////////////////////////////////////////////////////////////////////
	if (nFlags & FLAGS_PATH_REAL)
	{
		if (filehelpers::CheckPrefix( szNewSrc,"./" ) ||
				filehelpers::CheckPrefix( szNewSrc,".\\" ))
		{
			const int nLen = min(sizeof(szNewSrc), strlen(szNewSrc)-2);
			memmove( szNewSrc,szNewSrc+2,nLen );
			szNewSrc[nLen] = 0;
		}
	}
	//////////////////////////////////////////////////////////////////////////





	if (!isAbsolutePath && !(nFlags&FLAGS_PATH_REAL) && !bAliasWasUsed)

	{
		// This is a relative filename.
		// 1) /root/system.cfg
		// 2) /root/game/system.cfg
		// 3) /root/game/config/system.cfg

		/************************************************************************
		   For backwards compatibility we first check against the old folder
		   structure beginning with "languages" in that case we prefix the game
			 folder. If not we make sure to not prefix since the new localization
			 folder does not sit within the game folder structure anymore.
		************************************************************************/
#if defined(PS3) || defined(LINUX) || defined(CAFE)
		if (filehelpers::CheckPrefix( szNewSrc,"languages/") ||
			(!filehelpers::CheckPrefix( szNewSrc,m_strDataRootWithSlash.c_str() ) && 
			!filehelpers::CheckPrefix( szNewSrc,"./" ) &&
			!filehelpers::CheckPrefix( szNewSrc,"engine/" ) && 
			!filehelpers::CheckPrefix( szNewSrc,m_sLocalizationFolder.c_str() )))
#else
		if (filehelpers::CheckPrefix( szNewSrc,"languages\\") ||
			(!filehelpers::CheckPrefix( szNewSrc,m_strDataRootWithSlash.c_str() ) &&
			!filehelpers::CheckPrefix( szNewSrc,".\\" ) && 
			!filehelpers::CheckPrefix( szNewSrc,"editor\\" ) &&
			!filehelpers::CheckPrefix( szNewSrc,"engine\\" ) && 
			!filehelpers::CheckPrefix( szNewSrc,"mods\\" ) &&
			!filehelpers::CheckPrefix( szNewSrc,m_sLocalizationFolder.c_str() )))
#endif
		{
			// Add data folder prefix.
			memmove( szNewSrc+m_strDataRootWithSlash.length(),szNewSrc,strlen(szNewSrc)+1);
			memcpy( szNewSrc,m_strDataRootWithSlash.c_str(),m_strDataRootWithSlash.length() );			
		}
#if defined(PS3) || defined(LINUX) || defined(CAFE)
		else if (filehelpers::CheckPrefix( szNewSrc,"./" ))
#else
		else if (filehelpers::CheckPrefix( szNewSrc,".\\" ))
#endif
		{
			const int nLen = min(sizeof(szNewSrc), strlen(szNewSrc)-2);
			memmove( szNewSrc,szNewSrc+2,nLen );
			szNewSrc[nLen] = 0;
		}











#if defined(LINUX)
		//[K01]: add full path only if no game prefix
		if (!filehelpers::CheckPrefix(szNewSrc, m_strDataRootWithSlash.c_str()))
		{
			strcpy(dst, fopenwrapper_basedir);
			strcat(dst, "/");
			if(*szNewSrc == '/')
				strcat(dst, szNewSrc+1);//'/' already ends fopenwrapper_basedir
			else
				strcat(dst, szNewSrc);
		}
		else
		{
			// we are trying to read game folder so don't append full dir otherwise DirFind will not find our file
			strcpy(dst, szNewSrc);
		}


#else
		//PC code
		strcpy(dst, szNewSrc );
#endif

	}
	else
	{
		// This is a full filename.










		strcpy(dst, szNewSrc);

	}
	const int dstLen = strlen(dst);

	char* pEnd = dst + dstLen;

#if defined(LINUX)
	//we got to adjust the filename and fetch the case sensitive one
	char sourceName[MAX_PATH];
	strcpy(sourceName, dst);
	// Note: we'll copy the adjustedFilename even if the filename can not be
	// matched against an existing file. This is because getFilenameNoCase() will
	// adjust leading path components (e.g. ".../game/..." => ".../Game/...").
	GetFilenameNoCase(sourceName, dst, false);
#endif

#if defined(PS3) || defined(LINUX) || defined(CAFE)
	if ((nFlags & FLAGS_ADD_TRAILING_SLASH) && pEnd > dst && (pEnd[-1]!=g_cNativeSlash && pEnd[-1]!=g_cNonNativeSlash))
#else
	// p now points to the end of string
	if ((nFlags & FLAGS_ADD_TRAILING_SLASH) && pEnd > dst && pEnd[-1]!=g_cNativeSlash)
#endif
	{
		*pEnd = g_cNativeSlash;
		*++pEnd = '\0';
	}

	//CryLog("%s is changed to %s", src, dst);
	return dst; // the last MOD scanned, or the absolute path outside MasterCD
}
/*
FILETIME CCryPak::GetFileTime(const char * szFileName)
{
FILETIME writetime;
memset(&writetime, 0, sizeof(writetime));
#ifdef WIN32
HANDLE hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
::GetFileTime(hFile, NULL, NULL, &writetime);
CloseHandle(hFile);
}
#endif
return writetime;
}
*/
/*bool CCryPak::IsOutOfDate(const char * szCompiledName, const char * szMasterFile)
{
FILE * f = FOpen(szMasterFile,"rb");
if (f)
FClose(f);
else
return (false);

assert(f > m_OpenFiles.Num());

f = FOpen(szCompiledName,"rb");
if (f)
FClose(f);
else
return (true);

assert(f > m_OpenFiles.Num());

#ifdef WIN32

HANDLE status1 = CreateFile(szCompiledName,GENERIC_READ,FILE_SHARE_READ,
NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);

HANDLE status2 = CreateFile(szMasterFile,GENERIC_READ,FILE_SHARE_READ,
NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);

FILETIME writetime1,writetime2;

GetFileTime(status1,NULL,NULL,&writetime1);
GetFileTime(status2,NULL,NULL,&writetime2);

CloseHandle(status1);
CloseHandle(status2);

if (CompareFileTime(&writetime1,&writetime2)==-1)
return(true);

return (false);
#else

return (false);

#endif
}*/

//////////////////////////////////////////////////////////////////////////
bool CCryPak::IsFileExist( const char *sFilename, EFileSearchLocation fileLocation )
{
	// lock-less check
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_SYSTEM );

	char szFullPathBuf[g_nMaxPath];

	const int nVarPakPriority = m_pPakVars->nPriority;

	const char *szFullPath = AdjustFileName(sFilename, szFullPathBuf, FOPEN_HINT_QUIET);
	if (!szFullPath)
		return false;

	if (fileLocation == eFileLocation_InPak)
	{
		if (FindPakFileEntry(szFullPath))	// try to find the pseudo-file in one of the zips
			return true;
		return false;
	}

	if (nVarPakPriority == ePakPriorityFileFirst ||
		(nVarPakPriority == ePakPriorityFileFirstModsOnly && IsModPath(szFullPath))) // if the file system files have priority now..
	{
		if (filehelpers::CheckFileExistOnDisk(szFullPath))
		{
			return true;
		}
		else if ( fileLocation == eFileLocation_OnDisk )
		{
			return false;
		}
	}

	if (FindPakFileEntry(szFullPath))	// try to find the pseudo-file in one of the zips
		return true;

	if (nVarPakPriority == ePakPriorityPakFirst || eFileLocation_OnDisk == fileLocation) // if the pak files had more priority, we didn't attempt fopen before- try it now
	{
		if (filehelpers::CheckFileExistOnDisk(szFullPath))
			return true;
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CCryPak::IsFileCompressed(const char* filename)
{
	FUNCTION_PROFILER(gEnv->pSystem, PROFILE_SYSTEM);

	char fullPathBuf[g_nMaxPath];
	const char* pFullPath = AdjustFileName(filename, fullPathBuf, FOPEN_HINT_QUIET);
	if (!pFullPath)
		return false;

	if (m_pPakVars->nPriority == ePakPriorityFileFirst ||
		(m_pPakVars->nPriority == ePakPriorityFileFirstModsOnly && IsModPath(pFullPath)))
	{
		if (filehelpers::CheckFileExistOnDisk(pFullPath))
			return false;
	}

	ZipDir::FileEntry* pFileEntry = FindPakFileEntry(pFullPath);
	if (pFileEntry)
		return pFileEntry->nMethod != ZipFile::METHOD_STORE;

	return false;
}

//////////////////////////////////////////////////////////////////////////
FILE *CCryPak::FOpenRaw(const char *pName, const char *mode)
{
	LOADING_TIME_PROFILE_SECTION;
	PROFILE_DISK_OPEN;
	return CIOWrapper::Fopen(pName, mode);
}

//////////////////////////////////////////////////////////////////////////
FILE *CCryPak::FOpen(const char *pName, const char *szMode,char *szFileGamePath,int nLen)
{
	LOADING_TIME_PROFILE_SECTION;

	SAutoCollectFileAcessTime accessTime(this);

	PROFILE_DISK_OPEN;
	FILE *fp = NULL;
#if !defined(PS3)
	char szFullPathBuf[g_nMaxPath];
	const char* szFullPath = AdjustFileName(pName, szFullPathBuf, 0);
	if (nLen>g_nMaxPath)
		nLen=g_nMaxPath;
	strncpy(szFileGamePath,szFullPath,nLen);
	fp = CIOWrapper::Fopen(szFullPath, szMode);


#endif

	CheckFileAccessDisabled(pName, szMode);

	if (fp)
		RecordFile( fp, pName );
	else
		OnMissingFile(pName);
	return (fp);
}

//////////////////////////////////////////////////////////////////////////
FILE *CCryPak::FOpen(const char *pName, const char *szMode,unsigned nInputFlags)
{

	LOADING_TIME_PROFILE_SECTION;

	if(strlen(pName)>=g_nMaxPath)
		return 0;


	PROFILE_DISK_OPEN;
	SAutoCollectFileAcessTime accessTime(this);

	FILE *fp = NULL;
	char szFullPathBuf[g_nMaxPath];

	bool bFileCanBeOnDisk = true; //0 != (nInputFlags & FOPEN_ONDISK);

	// get the priority into local variable to avoid it changing in the course of
	// this function execution (?)
	int nVarPakPriority = m_pPakVars->nPriority;

	// Remove unknown to CRT 'x' parameter.
	char smode[16];
	strncpy( smode,szMode,sizeof(smode) );
	smode[sizeof(smode) - 1] = 0;
	for (char *s = smode; *s; s++) { if (*s == 'x' || *s == 'X') { *s = ' '; }; }

#if defined(LINUX)
	unsigned nOSFlags = _O_RDONLY;
#else
	unsigned nOSFlags = _O_BINARY|_O_RDONLY;
#endif

	//Timur, Try direct zip operation always.
	nInputFlags |= FOPEN_HINT_DIRECT_OPERATION;

	int nAdjustFlags = 0;

	// check the szMode
	for (const char* pModeChar = szMode; *pModeChar; ++pModeChar)
		switch (*pModeChar)
	{
		case 'r':
			nOSFlags &= ~(_O_WRONLY|_O_RDWR);
			// read mode is the only mode we can open the file in
			break;
		case 'w':
			nOSFlags |= _O_WRONLY;
			nAdjustFlags |= FLAGS_FOR_WRITING;
			break;
		case 'a':
			nOSFlags |= _O_RDWR;
			nAdjustFlags |= FLAGS_FOR_WRITING;
			break;
		case '+':
			nOSFlags |= _O_RDWR;
			nAdjustFlags |= FLAGS_FOR_WRITING;
			break;

		case 'b':
			nOSFlags &= ~_O_TEXT;
			nOSFlags |= _O_BINARY;
			break;
		case 't':
			nOSFlags &= ~_O_BINARY;
			nOSFlags |= _O_TEXT;
			break;

		case 'c':
		case 'C':
			nOSFlags |= (uint32)CZipPseudoFile::_O_COMMIT_FLUSH_MODE;
			break;
		case 'n':
		case 'N':
			nOSFlags &= ~CZipPseudoFile::_O_COMMIT_FLUSH_MODE;
			break;

		case 'S':
			nOSFlags |= _O_SEQUENTIAL;
			break;

		case 'R':
			nOSFlags |= _O_RANDOM;
			break;

		case 'T':
			nOSFlags |= _O_SHORT_LIVED;
			break;

		case 'D':
			nOSFlags |= _O_TEMPORARY;
			break;

		case 'x':
		case 'X':
			nInputFlags |= FOPEN_HINT_DIRECT_OPERATION;
			break;
	}

	const char *szFullPath = AdjustFileName(pName, szFullPathBuf, nAdjustFlags);

	if (nOSFlags & (_O_WRONLY|_O_RDWR))
	{
		CheckFileAccessDisabled(szFullPath, szMode);

		// we need to open the file for writing, but we failed to do so.
		// the only reason that can be is that there are no directories for that file.
		// now create those dirs
		if (!MakeDir (PathUtil::GetParentDirectory(string(szFullPath)).c_str()))
		{
			return NULL;
		}
		FILE *file = CIOWrapper::Fopen(szFullPath, smode);
		return file;
	}

	//////////////////////////////////////////////////////////////////////////
	if (g_cvars.pakVars.nReadAheadCache != 0 && 
			nOSFlags & (_O_RDONLY|_O_BINARY) &&
			g_cvars.pakVars.nSaveLevelResourceList == 0 &&
			(0 == (nInputFlags&FOPEN_FORSTREAMING)))
	{
		// Read ahead cache enabled.
		fp = m_pReadAheadCache->FOpen( pName );
		if (fp)
		{
			return fp;
		}
	}

	if (nVarPakPriority == ePakPriorityFileFirst ||
		(nVarPakPriority == ePakPriorityFileFirstModsOnly && IsModPath(szFullPath))) // if the file system files have priority now..
	{
		fp = CIOWrapper::Fopen(szFullPath, smode);
		if (fp)
		{
			CheckFileAccessDisabled(szFullPath, szMode);

			RecordFile( fp, pName );
			return fp;
		}
	}

	unsigned int archiveFlags;
	CCachedFileData_AutoPtr pFileData = GetFileData (szFullPath, archiveFlags);
	if (!pFileData)
	{
		if (nVarPakPriority != ePakPriorityPakOnly || bFileCanBeOnDisk) // if the pak files had more priority, we didn't attempt fopen before- try it now
		{
			fp = CIOWrapper::Fopen(szFullPath, smode);
			if (fp)
			{
				CheckFileAccessDisabled(szFullPath, szMode);

#if !defined (_RELEASE)
				RecordFile( fp, pName );
#endif
				return fp;
			}
		}
#if !defined (_RELEASE)
		if (!(nInputFlags&FOPEN_HINT_QUIET))
			OnMissingFile (pName);
#endif
		return NULL; // we can't find such file in the pack files
	}

	CheckFileAccessDisabled(szFullPath, szMode);

	// try to open the pseudofile from one of the zips, make sure there is no user alias for PS3
	AUTO_MODIFYLOCK(m_csOpenFiles);

	size_t nFile;
	FileAccessLogger log(CIOWrapper::IO_OPENINPAK, pName, 0, 0);
	// find the empty slot and open the file there; return the handle
	{
		for (nFile = 0; nFile < m_arrOpenFiles.size() && m_arrOpenFiles[nFile].GetFile(); ++nFile)
			continue;
		if (nFile == m_arrOpenFiles.size())
		{
			ScopedSwitchToGlobalHeap globalHeap;
			m_arrOpenFiles.resize (nFile+1);
		}
		if (pFileData != NULL && (nInputFlags & FOPEN_HINT_DIRECT_OPERATION)  && !pFileData->m_pZip->IsInMemory())
		{
			nOSFlags |= CZipPseudoFile::_O_DIRECT_OPERATION;
		}
		CZipPseudoFile& rZipFile = m_arrOpenFiles[nFile];
		nOSFlags |= (archiveFlags & FLAGS_REDIRECT_TO_DISC);
		rZipFile.Construct (pFileData, nOSFlags);
	}

	FILE *ret = (FILE*)(nFile + g_nPseudoFileIdxOffset);

#if !defined (_RELEASE)
	RecordFile( ret,pName );
#endif

	return ret; // the handle to the file
}

//////////////////////////////////////////////////////////////////////////
// given the file name, searches for the file entry among the zip files.
// if there's such file in one of the zips, then creates (or used cached)
// CCachedFileData instance and returns it.
// The file data object may be created in this function,
// and it's important that the autoptr is returned: another thread may release the existing
// cached data before the function returns
// the path must be absolute normalized lower-case with forward-slashes
CCachedFileDataPtr CCryPak::GetFileData(const char* szName, unsigned int& nArchiveFlags)
{
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_SYSTEM );

	CCachedFileData *pResult = 0;

	ZipDir::Cache *pZip = 0;
	ZipDir::FileEntry* pFileEntry = FindPakFileEntry( szName,nArchiveFlags,&pZip );
	if (pFileEntry)
	{
		pResult = new CCachedFileData (this, pZip, pFileEntry,szName);
		// Associate zip FileEntry with the filename for logging file access inside pak files.
		FileAccessLogger::RegisterFileHandle(pFileEntry,szName);
	}
	return pResult;
}

//////////////////////////////////////////////////////////////////////////
CCachedFileDataPtr CCryPak::GetOpenedFileDataInZip( FILE *hFile )
{
	AUTO_MODIFYLOCK(m_csCachedFiles);
	AUTO_READLOCK(m_csZips);

	INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
	if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
	{
		return m_arrOpenFiles[nPseudoFile].GetFile();
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
// tests if the given file path refers to an existing file inside registered (opened) packs
// the path must be absolute normalized lower-case with forward-slashes
ZipDir::FileEntry* CCryPak::FindPakFileEntry(const char* szPath,unsigned int& nArchiveFlags,ZipDir::Cache **pZip,bool bSkipInMemoryPaks)
{
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_SYSTEM );
	LOADING_TIME_PROFILE_SECTION;

#if defined(LINUX)
	// Timur, is it safe?
	//replaceDoublePathFilename((char*)szName);
#endif

	unsigned nNameLen = (unsigned)strlen(szPath);
	AUTO_READLOCK(m_csZips);
	// scan through registered pak files and try to find this file
	for (ZipArray::reverse_iterator itZip = m_arrZips.rbegin(); itZip != m_arrZips.rend(); ++itZip)
	{
    if (bSkipInMemoryPaks && itZip->pArchive->GetFlags() & ICryArchive::FLAGS_IN_MEMORY)
      continue;
	
		if( itZip->pArchive->GetFlags() & ICryArchive::FLAGS_DISABLE_PAK )
			continue;
	

		size_t nBindRootLen = itZip->strBindRoot.length();
#if defined(USE_COMMON_PATH_SPEEDUP)
		size_t nRootCompLength	 = itZip->strSharedRootPathName.length();
		const char* const cpRoot = itZip->strSharedRootPathName.c_str();
#else
		size_t nRootCompLength	 = itZip->strBindRoot.length();
		const char* const cpRoot = itZip->strBindRoot.c_str();
#endif

		if (nNameLen > nRootCompLength	&&!memcmp(cpRoot, szPath, nRootCompLength))
		{
#if defined(USE_COMMON_PATH_SPEEDUP)
			nBindRootLen = itZip->strBindRoot.length();
#else
			nBindRootLen = nRootCompLength;
#endif




			ZipDir::FileEntry* pFileEntry = itZip->pZip->FindFile (szPath+nBindRootLen);
			if (pFileEntry)
			{
				if (pZip)
					*pZip = itZip->pZip;

				//if (pZip)
					//CryLog( "Zip [%s] %s",itZip->pZip->GetFilePath(),szPath );
				nArchiveFlags = itZip->pArchive->GetFlags();
				return pFileEntry;
			}
		}
	}
	nArchiveFlags = 0;
	return NULL;
}

long CCryPak::FTell(FILE *hFile)
{
	AUTO_READLOCK(m_csOpenFiles);
	INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
	if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
	{
		return m_arrOpenFiles[nPseudoFile].FTell();
	}
	else
	{
		//////////////////////////////////////////////////////////////////////////
		if (m_pReadAheadCache->IsReadAheadFile( hFile ))
		{
			return (long)m_pReadAheadCache->FTell(hFile);
		}
		//////////////////////////////////////////////////////////////////////////

		return CIOWrapper::FTell(hFile);
	}
}

// returns the path to the archive in which the file was opened
const char* CCryPak::GetFileArchivePath (FILE* hFile)
{
	AUTO_READLOCK(m_csOpenFiles);
	INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
	if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
		return m_arrOpenFiles[nPseudoFile].GetArchivePath();
	else
		return NULL;
}

#ifndef Int32x32To64
#define Int32x32To64(a, b) ((uint64)((uint64)(a)) * (uint64)((uint64)(b)))
#endif

// returns the file modification time
uint64 CCryPak::GetModificationTime(FILE* hFile)
{
	INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
	{
		AUTO_READLOCK(m_csOpenFiles);
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
			return m_arrOpenFiles[nPseudoFile].GetModificationTime();
	}
	{
#if defined(LINUX) || defined(PS3)
		return GetFileModifTime(hFile);
#elif defined(WIN32) || defined(XENON)

		//////////////////////////////////////////////////////////////////////////
		if (m_pReadAheadCache->IsReadAheadFile( hFile ))
		{
			return 0;
		}
		//////////////////////////////////////////////////////////////////////////

		// Win 32
		HANDLE hOsFile = (void*)_get_osfhandle( fileno(hFile) );
		FILETIME CreationTime,LastAccessTime,LastWriteTime;

		GetFileTime( hOsFile,&CreationTime,&LastAccessTime,&LastWriteTime );
		LARGE_INTEGER lt;
		lt.HighPart = LastWriteTime.dwHighDateTime;
		lt.LowPart = LastWriteTime.dwLowDateTime;
		return lt.QuadPart;








#else
		Undefined!
#endif
	}
}

size_t CCryPak::FGetSize(FILE* hFile)
{
	INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
	{
		AUTO_READLOCK(m_csOpenFiles);
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
			return m_arrOpenFiles[nPseudoFile].GetFileSize();
	}

	//////////////////////////////////////////////////////////////////////////
	if (m_pReadAheadCache->IsReadAheadFile( hFile ))
	{
		return m_pReadAheadCache->FGetSize(hFile);
	}
	//////////////////////////////////////////////////////////////////////////




	struct stat buf;


	fstat( fileno(hFile), &buf );
	return buf.st_size;
}

//////////////////////////////////////////////////////////////////////////
size_t CCryPak::FGetSize(const char* sFilename, bool bAllowUseFileSystem)
{
	// lock-less GetSize
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_SYSTEM );

	char szFullPathBuf[g_nMaxPath];
	const char *szFullPath = AdjustFileName(sFilename, szFullPathBuf, FOPEN_HINT_QUIET);
	if (!szFullPath)
		return false;

	if (m_pPakVars->nPriority == ePakPriorityFileFirst ||
		(m_pPakVars->nPriority == ePakPriorityFileFirstModsOnly && IsModPath(szFullPath))) // if the file system files have priority now..
	{
		int64 nFileSize = filehelpers::GetFileSizeOnDisk(szFullPath);
		if (nFileSize >= 0)
			return (size_t)nFileSize;
	}

	ZipDir::FileEntry *pFileEntry = FindPakFileEntry(szFullPath);
	if (pFileEntry)	// try to find the pseudo-file in one of the zips
		return pFileEntry->desc.lSizeUncompressed;

	if (bAllowUseFileSystem || m_pPakVars->nPriority == ePakPriorityPakFirst) // if the pak files had more priority, we didn't attempt fopen before- try it now
	{
		int64 nFileSize = filehelpers::GetFileSizeOnDisk(szFullPath);
		if (nFileSize >= 0)
			return (size_t)nFileSize;
	}

	return 0;
}

int CCryPak::FFlush(FILE *hFile)
{
	SAutoCollectFileAcessTime accessTime(this);
	INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
	{
		AUTO_READLOCK(m_csOpenFiles);
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
			return 0;
	}
	//////////////////////////////////////////////////////////////////////////
	if (m_pReadAheadCache->IsReadAheadFile( hFile ))
	{
		// Do nothing.
		return 0;
	}
	//////////////////////////////////////////////////////////////////////////

	return fflush(hFile);
}

size_t CCryPak::FSeek(FILE *hFile, long seek, int mode)
{
	SAutoCollectFileAcessTime accessTime(this);

	{
		AUTO_READLOCK(m_csOpenFiles);
		INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
			return m_arrOpenFiles[nPseudoFile].FSeek(seek, mode);
	}

	//////////////////////////////////////////////////////////////////////////
	if (m_pReadAheadCache->IsReadAheadFile( hFile ))
	{
		return m_pReadAheadCache->FSeek(hFile,seek,mode);
	}
	//////////////////////////////////////////////////////////////////////////

	int nResult = CIOWrapper::Fseek(hFile, seek, mode);
	assert(nResult == 0);
	return nResult;
}

size_t CCryPak::FWrite(const void *data, size_t length, size_t elems, FILE *hFile)
{
	SAutoCollectFileAcessTime accessTime(this);

	INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
	{
		AUTO_READLOCK(m_csOpenFiles);
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
			return 0;
	}

	//////////////////////////////////////////////////////////////////////////
	if (m_pReadAheadCache->IsReadAheadFile( hFile ))
	{
		// Unsupported.
		assert(0);
		return 0;
	}
	//////////////////////////////////////////////////////////////////////////

	CRY_ASSERT(hFile);
	PROFILE_DISK_WRITE;
	return fwrite(data, length, elems, hFile);
}

//////////////////////////////////////////////////////////////////////////
size_t CCryPak::FReadRaw(void *pData, size_t nSize, size_t nCount, FILE *hFile)
{
	LOADING_TIME_PROFILE_SECTION;

	SAutoCollectFileAcessTime accessTime(this);

	{
		AUTO_READLOCK(m_csOpenFiles);
		INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
		{
			//FileAccessLogger log(CIOWrapper::IO_READINPAK, hFile, nSize*nCount,NULL, CIOWrapper::m_iLoggerEnabled);
			return m_arrOpenFiles[nPseudoFile].FRead(pData, nSize, nCount, hFile);
		}
	}

	//////////////////////////////////////////////////////////////////////////
	if (m_pReadAheadCache->IsReadAheadFile( hFile ))
	{
		return m_pReadAheadCache->FRead(hFile,pData,nSize,nCount);
	}
	//////////////////////////////////////////////////////////////////////////

	return CIOWrapper::Fread(pData, nSize, nCount, hFile);
}

//////////////////////////////////////////////////////////////////////////
size_t CCryPak::FReadRawAll(void *pData, size_t nFileSize, FILE *hFile)
{
	LOADING_TIME_PROFILE_SECTION;

	SAutoCollectFileAcessTime accessTime(this);
	{
		AUTO_READLOCK(m_csOpenFiles);
		INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size()) {
			//FileAccessLogger log(CIOWrapper::IO_READINPAK, hFile, nFileSize,"", CIOWrapper::m_iLoggerEnabled);
			return m_arrOpenFiles[nPseudoFile].FReadAll(pData, nFileSize, hFile);
		}
	}

	//////////////////////////////////////////////////////////////////////////
	if (m_pReadAheadCache->IsReadAheadFile( hFile ))
	{
		m_pReadAheadCache->FSeek(hFile,0,SEEK_SET);
		return m_pReadAheadCache->FRead(hFile,pData,1,nFileSize);
	}
	//////////////////////////////////////////////////////////////////////////

	int nRes = CIOWrapper::Fseek( hFile,0,SEEK_SET ); assert(nRes==0);
	return CIOWrapper::Fread(pData, 1, nFileSize, hFile);
}

//////////////////////////////////////////////////////////////////////////
void* CCryPak::FGetCachedFileData( FILE *hFile,size_t &nFileSize )
{
	LOADING_TIME_PROFILE_SECTION;

	SAutoCollectFileAcessTime accessTime(this);
	{
		AUTO_READLOCK(m_csOpenFiles);
		INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
			return m_arrOpenFiles[nPseudoFile].GetFileData( nFileSize,hFile );
	}

	//////////////////////////////////////////////////////////////////////////
	if (m_pReadAheadCache->IsReadAheadFile( hFile ))
	{
		return m_pReadAheadCache->FGetCachedFileData(hFile,nFileSize);
	}
	//////////////////////////////////////////////////////////////////////////

	if (m_pCachedFileData)
	{
		assert(0 && "Cannot have more then 1 FGetCachedFileData at the same time");
		CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_ERROR,"!Cannot have more then 1 FGetCachedFileData at the same time" );
		return 0;
	}




	struct stat buf;

	fstat( fileno(hFile), &buf );
	nFileSize = buf.st_size;

	m_pCachedFileData = new CCachedFileRawData(nFileSize);
	m_pCachedFileData->m_hFile = hFile;

	int nRes = CIOWrapper::Fseek( hFile,0,SEEK_SET ); assert(nRes==0);
	if (CIOWrapper::Fread(m_pCachedFileData->m_pCachedData, 1, nFileSize, hFile) != nFileSize)
	{
		m_pCachedFileData = 0;
		return 0;
	}
	return m_pCachedFileData->m_pCachedData;
}

//////////////////////////////////////////////////////////////////////////
int CCryPak::FClose(FILE *hFile)
{
	if (m_pCachedFileData && m_pCachedFileData->m_hFile == hFile) // Free cached data.
		m_pCachedFileData = 0;

	SAutoCollectFileAcessTime accessTime(this);
	INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
	AUTO_MODIFYLOCK(m_csOpenFiles);
	if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
	{
		m_arrOpenFiles[nPseudoFile].Destruct();
		return 0;
	}
	else
	{
		//////////////////////////////////////////////////////////////////////////
		if (m_pReadAheadCache->IsReadAheadFile( hFile ))
		{
			m_pReadAheadCache->FClose(hFile);
			return 0;
		}
		//////////////////////////////////////////////////////////////////////////

		return CIOWrapper::Fclose(hFile);
	}
}

bool CCryPak::IsInPak(FILE *hFile)
{
	AUTO_READLOCK(m_csOpenFiles);
	INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
	return (UINT_PTR)nPseudoFile < m_arrOpenFiles.size();
}

int CCryPak::FEof(FILE *hFile)
{
	SAutoCollectFileAcessTime accessTime(this);
	{
		AUTO_READLOCK(m_csOpenFiles);
		INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
			return m_arrOpenFiles[nPseudoFile].FEof();
	}
	//////////////////////////////////////////////////////////////////////////
	if (m_pReadAheadCache->IsReadAheadFile( hFile ))
	{
		return m_pReadAheadCache->FEof(hFile);
	}
	//////////////////////////////////////////////////////////////////////////

	return feof(hFile);
}

int CCryPak::FError(FILE *hFile)
{
	{
		AUTO_READLOCK(m_csOpenFiles);
		INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
			return 0;
	}
	//////////////////////////////////////////////////////////////////////////
	if (m_pReadAheadCache->IsReadAheadFile( hFile ))
	{
		return 0;
	}
	//////////////////////////////////////////////////////////////////////////
	return ferror(hFile);
}

int CCryPak::FGetErrno()
{
	return errno;
}

int CCryPak::FScanf(FILE *hFile, const char *format, ...)
{
	AUTO_READLOCK(m_csOpenFiles);
	SAutoCollectFileAcessTime accessTime(this);
	va_list arglist;
	int count = 0;
	va_start(arglist, format);
	INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
	if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
		count = m_arrOpenFiles[nPseudoFile].FScanfv(format, arglist);
	else
	{
		// Not supported.
		count = 0;//vfscanf(handle, format, arglist);
	}
	va_end(arglist);

	PROFILE_DISK_READ(count);

	return count;
}

int CCryPak::FPrintf(FILE *hFile, const char *szFormat, ...)
{
	SAutoCollectFileAcessTime accessTime(this);
	{
		AUTO_READLOCK(m_csOpenFiles);
		INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
			return 0; // we don't support it now
	}


	va_list arglist;
	int rv;
	va_start(arglist, szFormat);

	rv = vfprintf(hFile, szFormat, arglist);
	va_end(arglist);
	return rv;
}

char *CCryPak::FGets(char *str, int n, FILE *hFile)
{
	SAutoCollectFileAcessTime accessTime(this);
	{
		AUTO_READLOCK(m_csOpenFiles);
		INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
			return m_arrOpenFiles[nPseudoFile].FGets(str, n);
	}
	//////////////////////////////////////////////////////////////////////////
	if (m_pReadAheadCache->IsReadAheadFile( hFile ))
	{
		// Not supported
		assert(0);
		return "";
	}
	//////////////////////////////////////////////////////////////////////////

	PROFILE_DISK_READ(n);

	return fgets(str, n, hFile);
}

int CCryPak::Getc(FILE *hFile)
{
	SAutoCollectFileAcessTime accessTime(this);
	{
		AUTO_READLOCK(m_csOpenFiles);
		INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
			return m_arrOpenFiles[nPseudoFile].Getc();
	}

	//////////////////////////////////////////////////////////////////////////
	if (m_pReadAheadCache->IsReadAheadFile( hFile ))
	{
		// Not supported
		assert(0);
		return 0;
	}
	//////////////////////////////////////////////////////////////////////////

	PROFILE_DISK_READ(1);
	return getc(hFile);
}

int CCryPak::Ungetc(int c, FILE *hFile)
{
	SAutoCollectFileAcessTime accessTime(this);
	{
		AUTO_READLOCK(m_csOpenFiles);
		INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
		if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
			return m_arrOpenFiles[nPseudoFile].Ungetc(c);
	}
	//////////////////////////////////////////////////////////////////////////
	if (m_pReadAheadCache->IsReadAheadFile( hFile ))
	{
		// Not supported
		assert(0);
		return 0;
	}
	//////////////////////////////////////////////////////////////////////////

	return ungetc(c, hFile);
}

#ifndef _XBOX
const char *GetExtension (const char *in)
{
	while (*in)
	{
		if (*in=='.')
			return in;
		in++;
	}
	return NULL;
}


#endif //_XBOX

//////////////////////////////////////////////////////////////////////////
intptr_t CCryPak::FindFirst(const char *pDir, _finddata_t *fd,
														unsigned int nPathFlags, bool bAllOwUseFileSystem)
{
	char szFullPathBuf[g_nMaxPath];

	//m_pLog->Log("Scanning %s",pDir);
	//const char *szFullPath = AdjustFileName(pDir, szFullPathBuf, 0);
	const char *szFullPath = AdjustFileName(pDir, szFullPathBuf,nPathFlags);
	CCryPakFindData_AutoPtr pFindData = new CCryPakFindData(this, szFullPath, bAllOwUseFileSystem);
	if (pFindData->empty())
	{
		if (m_arrMods.empty())
			return (-1); // no mods and no files found
	}

	if (m_pPakVars->nPriority != ePakPriorityPakOnly)
	{
		// now scan mod folders as well

		std::vector<string>::reverse_iterator it;
		for (it = m_arrMods.rbegin(); it != m_arrMods.rend(); ++it)
		{
			if (m_pPakVars->nPriority == ePakPriorityFileFirstModsOnly && !IsModPath(it->c_str()))
				continue;
			CryPathString modPath = (*it).c_str();
			modPath.append(1,'/');
			modPath += pDir;
			const char *szFullModPath = AdjustFileName( modPath,szFullPathBuf,nPathFlags|FLAGS_PATH_REAL );
			if (szFullModPath)
			{
				pFindData->Scan(this,szFullModPath);
			}
		} //it
	}


	if (pFindData->empty())
		return (-1);

	AUTO_MODIFYLOCK(m_csFindData);
	m_setFindData.insert (pFindData);

	pFindData->Fetch(fd);

#ifdef LINUX
	//[K01]: you can't just cast a pointer...if it's negative, we'll think it failed!
	m_HandleSource++;
	if (m_HandleSource < 0)
		m_HandleSource = 1;
	assert(m_Handles.find(m_HandleSource) == m_Handles.end());
	m_Handles.insert(std::pair<intptr_t, CCryPakFindData*>(m_HandleSource, pFindData));
	return m_HandleSource;
#else
	return (intptr_t)(CCryPakFindData*)pFindData;
#endif
}

//////////////////////////////////////////////////////////////////////////
int CCryPak::FindNext(intptr_t handle, struct _finddata_t *fd)
{
	AUTO_READLOCK(m_csFindData);
	//if (m_setFindData.find ((CCryPakFindData*)handle) == m_setFindData.end())
	//	return -1; // invalid handle

#ifdef LINUX
	//[K01]: linux fixes
	std::map<intptr_t, CCryPakFindData*>::iterator lookup = m_Handles.find(handle);
	if (lookup == m_Handles.end())
		return -1;

	CCryPakFindData *CryFinder = lookup->second;
	if (CryFinder->Fetch(fd))
		return 0;
	else
		return -1;
#else
	if (((CCryPakFindData*)handle)->Fetch(fd))
		return 0;
	else
		return -1;
#endif
}

int CCryPak::FindClose(intptr_t handle)
{
	AUTO_MODIFYLOCK(m_csFindData);

#ifdef LINUX
	std::map<intptr_t, CCryPakFindData*>::iterator lookup = m_Handles.find(handle);
	if (lookup == m_Handles.end())
		return -1;
	CCryPakFindData *CryFinder = lookup->second;
	m_Handles.erase(lookup);

	m_setFindData.erase (CryFinder);
#else
	m_setFindData.erase ((CCryPakFindData*)handle);
#endif
	return 0;
}

//////////////////////////////////////////////////////////////////////////
bool CCryPak::LoadPakToMemory( const char *pName, bool bLoadToMemory, IMemoryBlock *pMemoryBlock )
{
	LOADING_TIME_PROFILE_SECTION;
	MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_Other, 0, "Load Pak To Memory: %s",pName );

	CryPathString pakFile = pName;
	pakFile.MakeLower();
	pakFile.replace( g_cNonNativeSlash,g_cNativeSlash );

	AUTO_MODIFYLOCK(m_csZips);
	for (ZipArray::iterator it = m_arrZips.begin(); it != m_arrZips.end(); ++it)
	{
		if (0 != strstr(it->GetFullPath(),pakFile.c_str()))
		{
			if (bLoadToMemory)
			{
				it->pZip->PreloadToMemory(pMemoryBlock);
			}
			else
			{
				it->pZip->UnloadFromMemory();
			}
			return true;
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CCryPak::LoadPaksToMemory( int nMaxPakSize,bool bLoadToMemory )
{
	LOADING_TIME_PROFILE_SECTION;
	MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "Load Paks To Memory" );
	AUTO_MODIFYLOCK(m_csZips);
	for (ZipArray::reverse_iterator itZip = m_arrZips.rbegin(); itZip != m_arrZips.rend(); ++itZip)
	{
		if (bLoadToMemory)
		{
			if ((int)itZip->pZip->GetZipFileSize() <= nMaxPakSize)
			{
				itZip->pZip->PreloadToMemory();
			}
		}
		else
		{
			// Unload
			itZip->pZip->UnloadFromMemory();
		}
	}
}

//======================================================================
bool CCryPak::OpenPack(const char* szBindRootIn, const char *szPath, unsigned nFlags, IMemoryBlock* pData)
{
	assert(szBindRootIn);
	char szFullPathBuf[g_nMaxPath];

	const char *szFullPath = AdjustFileName(szPath, szFullPathBuf, nFlags);

	char szBindRootBuf[g_nMaxPath];
	const char* szBindRoot = AdjustFileName(szBindRootIn, szBindRootBuf, FLAGS_ADD_TRAILING_SLASH|FLAGS_PATH_REAL);

	return OpenPackCommon(szBindRoot, szFullPath, nFlags, pData);
}

bool CCryPak::OpenPack(const char *szPath, unsigned nFlags, IMemoryBlock* pData)
{
	char szFullPathBuf[g_nMaxPath];

	const char *szFullPath = AdjustFileName(szPath, szFullPathBuf, nFlags);
	string strBindRoot;
	const char *pLastSlash = strrchr(szFullPath, g_cNativeSlash);
#if defined(LINUX) || defined(PS3) || defined(CAFE)
	if (!pLastSlash) pLastSlash = strrchr(szFullPath, g_cNonNativeSlash);
#endif
	if (pLastSlash)
		strBindRoot.assign (szFullPath, pLastSlash - szFullPath +  1);
	else
	{
		m_pLog->LogError("Pak file %s has absolute path %s, which is strange", szPath, szFullPath);
		//		desc.strFileName = szZipPath;
	}

	return OpenPackCommon(strBindRoot.c_str(), szFullPath, nFlags, pData);
}

bool CCryPak::OpenPackCommon(const char* szBindRoot, const char *szFullPath, unsigned int nPakFlags, IMemoryBlock* pData)
{
	// setup PackDesc before the duplicate test (since ps3 adjusts the BindRoot member, and thus the duplicate test won't find these then)
	PackDesc desc;










	desc.strBindRoot = szBindRoot;

	desc.strFileName = szFullPath;

	{
		AUTO_READLOCK(m_csZips);
		// try to find this - maybe the pack has already been opened
		for (ZipArray::iterator it = m_arrZips.begin(); it != m_arrZips.end(); ++it)
		{
			const char *pFilePath = it->pZip->GetFilePath();




			if (!stricmp(pFilePath, desc.strFileName.c_str())
				&&!stricmp(it->strBindRoot.c_str(), desc.strBindRoot.c_str()))
				return true; // already opened
		}
	}

#if defined(USE_COMMON_PATH_SPEEDUP)
	desc.strSharedRootPathName = desc.strBindRoot;
#endif

	int flags = ICryArchive::FLAGS_OPTIMIZED_READ_ONLY|ICryArchive::FLAGS_ABSOLUTE_PATHS;
	if ((nPakFlags & FLAGS_PAK_IN_MEMORY) != 0)
		flags |= ICryArchive::FLAGS_IN_MEMORY;
	if ((nPakFlags & FLAGS_FILENAMES_AS_CRC32) != 0)
		flags |= ICryArchive::FLAGS_FILENAMES_AS_CRC32;
	if ((nPakFlags & FLAGS_REDIRECT_TO_DISC) != 0)
		flags |= FLAGS_REDIRECT_TO_DISC;
	if ((nPakFlags & ICryArchive::FLAGS_OVERRIDE_PAK) != 0)
		flags |= ICryArchive::FLAGS_OVERRIDE_PAK;

	desc.pArchive = OpenArchive (szFullPath, flags, pData);
	if (!desc.pArchive)
		return false; // couldn't open the archive

	//Check for whitelisting
	uint32 fullPathCRC = 0;
	if(m_bUseWhiteList == true)
	{
		const char *szFullPathNoDrive = strchr( szFullPath, ':' );
		szFullPathNoDrive = ( szFullPathNoDrive == NULL ? szFullPath : szFullPathNoDrive+1 );
		fullPathCRC = crc32(0,(const Bytef *)szFullPathNoDrive,strlen(szFullPathNoDrive));
		//Test for the presence of the filename in the map that defines the whitelist
		if( m_whiteList.find(fullPathCRC) == m_whiteList.end() )
		{
			CryWarning( VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Pakfile %s was requested to open but is not present in the whitelist. This file cannot be opened while the whitelist is enabled.", szFullPath );
			//Invoke the global handler in PlatformOS
			IPlatformOS *pPlatformOS = gEnv->pSystem->GetPlatformOS();
			if( pPlatformOS != NULL )
			{
				pPlatformOS->HandleArchiveVerificationFailure( );
			}
			else
			{
				//Notify the POS about the detected corruption so it can handle it
				CryWarning( VALIDATOR_MODULE_SYSTEM, VALIDATOR_COMMENT, "Issue detected before PlatformOS has been initialized. Setting a flag so that it can respond when ready." );
				gEnv->pSystem->AddPlatformOSCreateFlag( ( uint8 )IPlatformOS::eCF_EarlyCorruptionDetected );
			}
			return false;
		}
	}

	AUTO_MODIFYLOCK(m_csZips);

	CryComment("Opening pak file %s",szFullPath);	

	if (desc.pArchive->GetClassId() == CryArchive::gClassId)
	{
		m_pLog->LogWithType(IMiniLog::eComment,"Opening pak file %s to %s",szFullPath,szBindRoot?szBindRoot:"<NIL>");
		desc.pZip = static_cast<CryArchive*>((ICryArchive*)desc.pArchive)->GetCache();

#if defined(USE_COMMON_PATH_SPEEDUP)
		//get the path shared by all files for faster file lookups
		ZipDir::FindFile fd (desc.pZip);
		fd.FindFirst ("*"); 
		if(!fd.GetFileEntry())
		{
			//there are no files in the root path, check if there is just one main directory
			ZipDir::FindDir fd (desc.pZip);
			const char* cpFirstDirName = NULL;
			for (fd.FindFirst("*"); fd.GetDirEntry(); fd.FindNext())
			{
				if(!cpFirstDirName)
					cpFirstDirName = fd.GetDirName();
				else
				{
					//there are several root directories
					cpFirstDirName = NULL;
					break;
				}
			}
			if(cpFirstDirName)
			{
				desc.strSharedRootPathName += cpFirstDirName;
				desc.strSharedRootPathName += "/";
			}
		}
#endif
		//Append the pak to the end but before any override paks
		ZipArray::reverse_iterator revItZip = m_arrZips.rbegin();
		if ((nPakFlags & ICryArchive::FLAGS_OVERRIDE_PAK) == 0)
		{
			for (; revItZip != m_arrZips.rend(); ++revItZip)
			{
				if ((revItZip->pArchive->GetFlags() & ICryArchive::FLAGS_OVERRIDE_PAK) == 0)
				{
					break;
				}
			}
		}
		ZipArray::iterator itZipPlace = revItZip.base();
		m_arrZips.insert(itZipPlace, desc);










		//Perform CRC checking if desired
		if(m_bUseWhiteList && m_bUseWhiteListCRCs)
		{
			TWhiteListMap::const_iterator whiteListIt = m_whiteList.find(fullPathCRC);
			//Compute the archive CRC32
			uint32 computedCRC = ComputeCachedPakCDR_CRC(szFullPath, false);
			if( whiteListIt->second != computedCRC )
			{
				CryWarning( VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Pakfile %s has a different header CRC to the stored version in the whitelist. %8X != %8X", szFullPath, computedCRC, whiteListIt->second );
				//Invoke the global handler in PlatformOS, close the pak and return false.
				IPlatformOS *pPlatformOS = gEnv->pSystem->GetPlatformOS();
				if( pPlatformOS != NULL )
				{
					pPlatformOS->HandleArchiveVerificationFailure( );
				}
				else
				{
					//Notify the POS about the detected corruption so it can handle it
					CryWarning( VALIDATOR_MODULE_SYSTEM, VALIDATOR_COMMENT, "Issue detected before PlatformOS has been initialized. Setting a flag so that it can respond when ready." );
					gEnv->pSystem->AddPlatformOSCreateFlag( ( uint8 )IPlatformOS::eCF_EarlyCorruptionDetected );
				}
				ClosePack(szFullPath);
				return false;
			}
		}

		return true;
	}
	else
		return false; // don't support such objects yet
}

//int gg=1;
// after this call, the file will be unlocked and closed, and its contents won't be used to search for files
bool CCryPak::ClosePack(const char* pName, unsigned nFlags)
{
	char szZipPathBuf[g_nMaxPath];
	const char* szZipPath = AdjustFileName(pName, szZipPathBuf, nFlags);

	//if (strstr(szZipPath,"huggy_tweak_scripts"))
	//	gg=0;

	AUTO_MODIFYLOCK(m_csZips);
	for (ZipArray::iterator it = m_arrZips.begin(); it != m_arrZips.end(); ++it)
	{
		if (!stricmp(szZipPath, it->GetFullPath()))
		{
			// this is the pack with the given name - remove it, and if possible it will be deleted
			// the zip is referenced from the archive and *it; the archive is referenced only from *it
			//
			// the pZip (cache) can be referenced from stream engine and pseudo-files.
			// the archive can be referenced from outside
			bool bResult = (it->pZip->NumRefs() == 2) && it->pArchive->NumRefs() == 1;
			if (bResult)
			{
				m_arrZips.erase (it);
			}








			return bResult;
		}
	}
	return true;
}

bool CCryPak::FindPacks(const char *pWildcardIn)
{
	char cWorkBuf[g_nMaxPath];
	AdjustFileName(pWildcardIn, cWorkBuf, ICryPak::FLAGS_PATH_REAL|ICryArchive::FLAGS_OVERRIDE_PAK|FLAGS_COPY_DEST_ALWAYS);
	string strBindRoot = PathUtil::GetParentDirectory(cWorkBuf);
	strBindRoot += g_cNativeSlash;
	__finddata64_t fd;
	intptr_t h = _findfirst64 (cWorkBuf, &fd);
	_findclose (h);
	return h != -1;
}

bool CCryPak::OpenPacks(const char *pWildcardIn, unsigned nFlags)
{
	char cWorkBuf[g_nMaxPath];
	AdjustFileName(pWildcardIn, cWorkBuf, nFlags|FLAGS_COPY_DEST_ALWAYS);
	string strBindRoot = PathUtil::GetParentDirectory(cWorkBuf);
	strBindRoot += g_cNativeSlash;
	return OpenPacksCommon(strBindRoot.c_str(), pWildcardIn, cWorkBuf,nFlags);
}

bool CCryPak::OpenPacks(const char* szBindRoot, const char *pWildcardIn, unsigned nFlags)
{
	char cWorkBuf[g_nMaxPath];
	AdjustFileName(pWildcardIn, cWorkBuf, nFlags|FLAGS_COPY_DEST_ALWAYS);



	char cBindRootBuf[g_nMaxPath];
	const char* pBindRoot = AdjustFileName(szBindRoot, cBindRootBuf, FLAGS_ADD_TRAILING_SLASH|FLAGS_PATH_REAL);

	return OpenPacksCommon(pBindRoot, pWildcardIn, cWorkBuf,nFlags);
}

bool CCryPak::OpenPacksCommon(const char* szDir, const char *pWildcardIn, char *cWork,int nPakFlags)
{
	// Note this code suffers from a common FindFirstFile problem - a search
	// for *.pak will also find *.pak? (since the short filename version of *.pakx,
	// for example, is *.pak). For more information, see
	// http://blogs.msdn.com/oldnewthing/archive/2005/07/20/440918.aspx
	// Therefore this code performs an additional check to make sure that the
	// found filenames match the spec.
	__finddata64_t fd;
	intptr_t h = _findfirst64 (cWork, &fd);

	char cWildcardFullPath[MAX_PATH];
	sprintf_s(cWildcardFullPath, "*.%s", PathUtil::GetExt(pWildcardIn) );

	// where to copy the filenames to form the path in cWork
	char* pDestName = strrchr (cWork, g_cNativeSlash);
#if defined(LINUX) || defined(PS3) || defined(CAFE)
	if (!pDestName) pDestName = strrchr (cWork, g_cNonNativeSlash);
#endif
	if (!pDestName)
		pDestName = cWork;
	else
		++pDestName;
	if (h != -1)
	{
		std::vector<string> files;
		do 
		{
			strcpy (pDestName, fd.name);
			if (PathUtil::MatchWildcard(cWork, cWildcardFullPath))
				files.push_back(strlwr(cWork));
		}
		while(0 == _findnext64 (h, &fd));

		// Open files in alphabet order.
		std::sort( files.begin(),files.end() );
		for (int i = 0; i < (int)files.size(); i++)
		{
			OpenPackCommon(szDir, files[i].c_str(), nPakFlags);
		}

		_findclose (h);
		return true;
	}

	return false;
}


bool CCryPak::ClosePacks(const char *pWildcardIn, unsigned nFlags)
{
	__finddata64_t fd;
	char cWorkBuf[g_nMaxPath];
	const char* cWork = AdjustFileName(pWildcardIn, cWorkBuf, nFlags);
	intptr_t h = _findfirst64 (cWork, &fd);
	string strDir = PathUtil::GetParentDirectory(pWildcardIn);
	if (h != -1)
	{
		do {
			ClosePack((strDir + "\\" + fd.name).c_str(), nFlags);
		} while(0 == _findnext64 (h, &fd));
		_findclose (h);
		return true;
	}
	return false;
}

int CCryPak::RemountPacks(DynArray<FILE*>& outHandlesToClose, CBShouldPackReOpen pShouldPackReOpen)
{
	AUTO_MODIFYLOCK(m_csZips);

	outHandlesToClose.reserve( m_arrZips.size() );

	int numRemounted = 0;
	ZipArray::iterator end = m_arrZips.end();
	for (ZipArray::iterator it = m_arrZips.begin(); it != end; ++it)
	{
		if (!(it->pArchive->GetFlags() & ICryArchive::FLAGS_IN_MEMORY))
		{
			ZipDir::Cache* pZip = it->pZip;
			const char* curPath = pZip->GetFilePath();
			
			if (pShouldPackReOpen( curPath ))
			{
				outHandlesToClose.push_back( pZip->GetFileHandle() );
				bool okay = pZip->ReOpen( curPath );

				ICryArchive* pArchive = it->pArchive;
				if (pArchive)
				{
					unsigned int eFlags = pArchive->GetFlags();
					pArchive->SetFlags(eFlags | ICryArchive::FLAGS_ON_HDD);
				}

				if (!okay)
				{
					CryFatalError( "RemountPacks: Failed to reopen pak %s\n", curPath );
				}
				numRemounted++;
			}
		}
	}
	return numRemounted;
}


bool CCryPak::InitPack(const char *szBasePath, unsigned nFlags)
{
	return true;
}

/////////////////////////////////////////////////////
bool CCryPak::Init(const char *szBasePath)
{
	return InitPack(szBasePath);
}

void CCryPak::Release()
{
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
CCachedFileRawData::CCachedFileRawData( int nAlloc )
{ 
	m_hFile = 0;
	m_pCachedData = 0;

	m_pCachedData = g_pPakHeap->TempAlloc(nAlloc,"CCachedFileRawData::CCachedFileRawData");
}

//////////////////////////////////////////////////////////////////////////
CCachedFileRawData::~CCachedFileRawData()
{
	if (m_pCachedData)
		g_pPakHeap->FreeTemporary(m_pCachedData);
	m_pCachedData = 0;
}


//////////////////////////////////////////////////////////////////////////
// this object must be constructed before usage
// nFlags is a combination of _O_... flags
void CZipPseudoFile::Construct(CCachedFileData* pFileData, unsigned nFlags)
{
	m_pFileData = pFileData;
	m_nFlags = nFlags;
	m_nCurSeek = 0;
}

//////////////////////////////////////////////////////////////////////////
// this object needs to be freed manually when the CryPak shuts down..
void CZipPseudoFile::Destruct()
{
	assert (m_pFileData != NULL);
	// mark it free, and deallocate the pseudo file memory
	m_pFileData = NULL;
}

//////////////////////////////////////////////////////////////////////////
int CZipPseudoFile::FSeek (long nOffset, int nMode)
{
	if (!m_pFileData)
		return -1;

	switch (nMode)
	{
	case SEEK_SET:
		m_nCurSeek = nOffset;
		break;
	case SEEK_CUR:
		m_nCurSeek += nOffset;
		break;
	case SEEK_END:
		m_nCurSeek = GetFileSize() - nOffset;
		break;
	default:
		assert(0);
		return -1;
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
size_t CZipPseudoFile::FRead (void* pDest, size_t nSize, size_t nCount, FILE* hFile)
{
	LOADING_TIME_PROFILE_SECTION;

	if (!GetFile())
		return 0;

	size_t nTotal = nSize * nCount;
	if (!nTotal || (unsigned)m_nCurSeek >= GetFileSize())
		return 0;

	if (nTotal > GetFileSize() - m_nCurSeek)
	{
		nTotal = GetFileSize() - m_nCurSeek;
		if (nTotal < nSize)
			return 0;
		nTotal -= nTotal % nSize;
	}

	{
		if (!(m_nFlags & _O_TEXT))
		{
			int64 nReadBytes = GetFile()->ReadData( pDest,m_nCurSeek,nTotal );
			if (nReadBytes != nTotal)
			{
				assert(0);
				CryFatalError("FRead did not read expected number of byte from file, only %d of %lld bytes read",nTotal,nReadBytes );
			}
			m_nCurSeek += nTotal;
		}
		else
		{
			unsigned char * pSrc = (unsigned char *)GetFile()->GetData();
			if (!pSrc)
				return 0;
			pSrc += m_nCurSeek;
			m_nCurSeek += nTotal;

			unsigned char *itDest = (unsigned char *)pDest;
			unsigned char *itSrc = pSrc, *itSrcEnd = pSrc + nTotal;
			nTotal = 0;
			for (; itSrc != itSrcEnd; ++itSrc)
			{
				if (*itSrc != 0xd)
				{
					*(itDest++) = *itSrc;
					nTotal++;
				}
			}
		}
	}
	return nTotal / nSize;
}

//////////////////////////////////////////////////////////////////////////
size_t CZipPseudoFile::FReadAll (void* pDest, size_t nFileSize, FILE* hFile)
{
	LOADING_TIME_PROFILE_SECTION;

	if (!GetFile())
		return 0;

	if (nFileSize != GetFileSize())
	{
		assert(0); // Bad call
		return 0;
	}

	if (!GetFile()->ReadData( pDest,0,nFileSize ))
		return 0;

	m_nCurSeek = nFileSize;

	return nFileSize;
}

//////////////////////////////////////////////////////////////////////////
void* CZipPseudoFile::GetFileData ( size_t &nFileSize, FILE* hFile )
{
	LOADING_TIME_PROFILE_SECTION;

	if (!GetFile())
		return 0;

	nFileSize = GetFileSize();

	void *pData = GetFile()->GetData();
	m_nCurSeek = nFileSize;
	return pData;
}

//////////////////////////////////////////////////////////////////////////
int CZipPseudoFile::FEof()
{
	return (unsigned)m_nCurSeek >= GetFileSize();
}

int CZipPseudoFile::FScanfv (const char* szFormat, va_list args)
{
	if (!GetFile())
		return 0;
	char *pSrc = (char *)GetFile()->GetData();
	if (!pSrc)
		return 0;
	// now scan the pSrc+m_nCurSeek
	return 0;
}

char* CZipPseudoFile::FGets(char* pBuf, int n)
{
	if (!GetFile())
		return NULL;

	char *pData = (char *)GetFile()->GetData();
	if (!pData)
		return NULL;
	int nn = 0;
	int i;
	for (i=0; i<n; i++)
	{
		if (i+m_nCurSeek == GetFileSize())
			break;
		char c = pData[i+m_nCurSeek];
		if (c == 0xa || c == 0)
		{
			pBuf[nn++] = c;
			i++;
			break;
		}
		else
			if (c == 0xd)
				continue;
		pBuf[nn++] = c;
	}
	pBuf[nn] = 0;
	m_nCurSeek += i;

	if (m_nCurSeek == GetFileSize())
		return NULL;
	return pBuf;
}

int CZipPseudoFile::Getc()
{
	if (!GetFile())
		return EOF;
	char *pData = (char *)GetFile()->GetData();
	if (!pData)
		return EOF;
	int c = EOF;
	int i;
	for (i=0; i<1; i++)
	{
		if (i+m_nCurSeek == GetFileSize())
			return c;
		c = pData[i+m_nCurSeek];
		break;
	}
	m_nCurSeek += i+1;
	return c;
}

int CZipPseudoFile::Ungetc(int c)
{
	if (m_nCurSeek <= 0)
		return EOF;
	m_nCurSeek--;
	return c;
}

CCachedFileData::CCachedFileData(class CCryPak* pPak, ZipDir::Cache*pZip, ZipDir::FileEntry* pFileEntry,const char *szFilename)
{
	m_pPak = pPak;
	m_pFileData = NULL;
	m_pZip = pZip;
	m_pFileEntry = pFileEntry;

#ifdef _DEBUG
	m_sDebugFilename = szFilename;
#endif _DEBUG
	//m_filename = szFilename;

	m_bDecompressed = false;
	if(pFileEntry)
		m_bDecompressed = pFileEntry->nMethod == ZipFile::METHOD_STORE;

	if (pPak)
		pPak->Register (this);
}

CCachedFileData::~CCachedFileData()
{
	if (m_pPak)
		m_pPak->Unregister (this);

	// forced destruction
	AUTO_LOCK_CS(m_csDecompressLock);
	if (m_pFileData)
	{
		g_pPakHeap->FreeTemporary(m_pFileData);
		m_pFileData = NULL;
	}

	m_pZip = NULL;
	m_pFileEntry = NULL;
}

//////////////////////////////////////////////////////////////////////////
bool CCachedFileData::GetDataTo( void *pFileData,int nDataSize,bool bDecompress )
{
	assert (m_pZip != NULL);
	assert (m_pFileEntry && m_pZip->IsOwnerOf(m_pFileEntry));

	if (nDataSize != m_pFileEntry->desc.lSizeUncompressed && bDecompress)
		return false;
	else if (nDataSize != m_pFileEntry->desc.lSizeCompressed && !bDecompress)
		return false;

	if (!m_pFileData)
	{
		AUTO_LOCK_CS(m_csDecompressLock);
		if (!m_pFileData)
		{
			if (ZipDir::ZD_ERROR_SUCCESS != m_pZip->ReadFile (m_pFileEntry, NULL, pFileData,bDecompress))
			{
				return false;
			}
		}
		else
		{
			memcpy( pFileData,m_pFileData,nDataSize );
		}
	}
	else
	{
		memcpy( pFileData,m_pFileData,nDataSize );
	}
	return true;
}

// return the data in the file, or NULL if error
void* CCachedFileData::GetData(bool bRefreshCache, const bool decompress/* = true*/, const bool allocateForDecompressed/* = true*/)
{
	// first, do a "dirty" fast check without locking the critical section
	// in most cases, the data's going to be already there, and if it's there,
	// nobody's going to release it until this object is destructed.
	if (bRefreshCache && !m_pFileData)
	{
		assert (m_pZip != NULL);
		assert (m_pFileEntry && m_pZip->IsOwnerOf(m_pFileEntry));
		// Then, lock it and check whether the data is still not there.
		// if it's not, allocate memory and unpack the file
		AUTO_LOCK_CS(m_csDecompressLock);
		if (!m_pFileData)
		{
			if(decompress)
			{
				assert(!m_bDecompressed || m_pFileEntry->nMethod == ZipFile::METHOD_STORE);
			}
			uint32 nTempBufferSize = (allocateForDecompressed) ? m_pFileEntry->desc.lSizeUncompressed : m_pFileEntry->desc.lSizeCompressed; 
			void *fileData = g_pPakHeap->TempAlloc (nTempBufferSize, "CCachedFileData::GetData");
			if (ZipDir::ZD_ERROR_SUCCESS != m_pZip->ReadFile (m_pFileEntry, NULL, fileData, decompress))
			{
				g_pPakHeap->FreeTemporary(fileData);
			}
			else
			{
				m_pFileData = fileData;
				if(decompress)
					m_bDecompressed = true;
			}
		}
	}
	return m_pFileData;
}

//////////////////////////////////////////////////////////////////////////
int64 CCachedFileData::ReadData( void *pBuffer,int64 nFileOffset,int64 nReadSize )
{
	if (!m_pFileEntry)
		return -1;

	int64 nFileSize = m_pFileEntry->desc.lSizeUncompressed;
	if (nFileOffset + nReadSize > nFileSize)
	{
		nReadSize = nFileSize - nFileOffset;
	}
	if (nReadSize < 0)
		return -1;

	if (nReadSize == 0)
		return 0;
	
	if (m_pFileEntry->nMethod == ZipFile::METHOD_STORE)
	{
		AUTO_LOCK_CS(m_csDecompressLock);
		// Uncompressed read.
		if (ZipDir::ZD_ERROR_SUCCESS != m_pZip->ReadFile(m_pFileEntry,NULL,pBuffer,false,nFileOffset,nReadSize ))
		{
			return -1;
		}
	}
	else
	{
		uint8 *pSrcBuffer = (uint8*)GetData();
		pSrcBuffer += nFileOffset;
		memcpy( pBuffer,pSrcBuffer,(size_t)nReadSize );
	}
	
	return nReadSize;
}


//////////////////////////////////////////////////////////////////////////
void CCachedFileData::AddRef()
{	
	CryInterlockedIncrement(&m_nRefCounter);
}

//////////////////////////////////////////////////////////////////////////
void CCachedFileData::Release()
{
	const int nCount = CryInterlockedDecrement(&m_nRefCounter);
	assert(nCount >= 0);
	if (nCount == 0)
	{
		AUTO_MODIFYLOCK(m_pPak->m_csCachedFiles);
		if( m_nRefCounter == 0)
			delete this;
	}
	else if (nCount < 0)
	{
		assert(0);
		CryFatalError( "Deleting Reference Counted Object Twice" );
	}
	
}

//////////////////////////////////////////////////////////////////////////
void CCryPakFindData::Scan(class CCryPak*pPak, const char* szDir, bool bAllowUseFS)
{
	// get the priority into local variable to avoid it changing in the course of
	// this function execution
	int nVarPakPriority = pPak->m_pPakVars->nPriority;

	if (nVarPakPriority == ePakPriorityFileFirst)
	{
		// first, find the file system files
		ScanFS(pPak,szDir);
		ScanZips(pPak, szDir);
	}
	else
	{
		// first, find the zip files
		ScanZips(pPak, szDir);
		if (bAllowUseFS ||
			(nVarPakPriority != ePakPriorityPakOnly && (nVarPakPriority != ePakPriorityFileFirstModsOnly || IsModPath(szDir))))
		{
			ScanFS(pPak,szDir);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
CCryPakFindData::CCryPakFindData (class CCryPak*pPak, 
																	const char* szDir, 
																	bool bAllowUseFS)
{
	Scan(pPak,szDir, bAllowUseFS);
}

//////////////////////////////////////////////////////////////////////////
void CCryPakFindData::ScanFS(CCryPak*pPak, const char *szDirIn)
{
	//char cWork[CCryPak::g_nMaxPath];
	//pPak->AdjustFileName(szDirIn, cWork);

#if defined(WIN32) || defined(WIN64)

	_wfinddata64_t fdw;
	__finddata64_t fd;
#ifdef WIN64
	memset (&fdw, 0, sizeof(fdw));
	memset (&fd, 0, sizeof(fd));
#endif
	wstring szDirInW = CryStringUtils::UTF8ToWStr(szDirIn);
	intptr_t nFS = _wfindfirst64(szDirInW, &fdw);
	if (nFS == -1)
		return;

	do
	{
		fd.attrib = fdw.attrib;
		fd.size = fdw.size;
		fd.time_access = fdw.time_access;
		fd.time_create = fdw.time_create;
		fd.time_write = fdw.time_write;
		strcpy_s(fd.name, CryStringUtils::WStrToUTF8(fdw.name));
		m_mapFiles.insert (FileMap::value_type(fd.name, FileDesc(&fd)));
	}
	while(0 == _wfindnext64(nFS, &fdw));

	_findclose (nFS);

#else

	__finddata64_t fd;
#ifdef WIN64
	memset (&fd, 0, sizeof(fd));
#endif
	intptr_t nFS = _findfirst64 (szDirIn, &fd);
	if (nFS == -1)
		return;

	do
	{
		m_mapFiles.insert (FileMap::value_type(fd.name, FileDesc(&fd)));
	}
	while(0 == _findnext64(nFS, &fd));

	_findclose (nFS);

#endif
}

//////////////////////////////////////////////////////////////////////////
void CCryPakFindData::ScanZips (CCryPak* pPak, const char* szDir)
{
	AUTO_READLOCK(pPak->m_csZips);

	size_t nLen = strlen(szDir);
	for (CCryPak::ZipArray::iterator it = pPak->m_arrZips.begin(); it != pPak->m_arrZips.end(); ++it)
	{
#if defined(USE_COMMON_PATH_SPEEDUP)
		size_t nRootCompLength	 = it->strSharedRootPathName.length();
		const char* const cpRoot = it->strSharedRootPathName.c_str();
#else
		size_t nRootCompLength	 = it->strBindRoot.length();
		const char* const cpRoot = it->strBindRoot.c_str();
#endif

		if (nLen > nRootCompLength && !memcmp(szDir, cpRoot, nRootCompLength))
		{
#if defined(USE_COMMON_PATH_SPEEDUP)
			size_t nBindRootLen = it->strBindRoot.length();
#else
			size_t nBindRootLen = nRootCompLength;
#endif
			// first, find the files
			{
				ZipDir::FindFile fd (it->pZip);
				for(fd.FindFirst (szDir + nBindRootLen); fd.GetFileEntry(); fd.FindNext())
					m_mapFiles.insert (FileMap::value_type(fd.GetFileName(), FileDesc(fd.GetFileEntry())));
			}

			{
				ZipDir::FindDir fd (it->pZip);
				for (fd.FindFirst(szDir + nBindRootLen); fd.GetDirEntry(); fd.FindNext())
					m_mapFiles.insert (FileMap::value_type(fd.GetDirName(), FileDesc ()));
			}
		}
	}
}

bool CCryPakFindData::empty() const
{
	return m_mapFiles.empty();
}

bool CCryPakFindData::Fetch(_finddata_t* pfd)
{
	if (m_mapFiles.empty())
		return false;

	FileMap::iterator it = m_mapFiles.begin();
	memcpy(pfd->name, it->first.c_str(), min((uint32)sizeof(pfd->name), (uint32)(it->first.length()+1)));
	pfd->attrib = it->second.nAttrib;
	pfd->size   = it->second.nSize;
	pfd->time_access = it->second.tAccess;
	pfd->time_create = it->second.tCreate;
	pfd->time_write  = it->second.tWrite;

	m_mapFiles.erase (it);
	return true;
}

CCryPakFindData::FileDesc::FileDesc (struct _finddata_t* fd)
{
	nSize   = fd->size;
	nAttrib = fd->attrib;
	tAccess = fd->time_access;
	tCreate = fd->time_create;
	tWrite  = fd->time_write;
}

// the conversions in this function imply that we don't support
// 64-bit file sizes or 64-bit time values
CCryPakFindData::FileDesc::FileDesc (struct __finddata64_t* fd)
{
	nSize   = (unsigned)fd->size;
	nAttrib = fd->attrib;
	tAccess = (time_t)fd->time_access;
	tCreate = (time_t)fd->time_create;
	tWrite  = (time_t)fd->time_write;
}


CCryPakFindData::FileDesc::FileDesc (ZipDir::FileEntry* fe)
{
	nSize = fe->desc.lSizeUncompressed;
#if defined(LINUX) || defined(PS3)
	nAttrib = _A_IN_CRYPAK; // files in zip are read-only, and
#else
	nAttrib = _A_RDONLY|_A_IN_CRYPAK; // files in zip are read-only, and
#endif
	tAccess = -1;
	tCreate = -1;
	tWrite	= fe->GetModificationTime();
}

CCryPakFindData::FileDesc::FileDesc ()
{
	nSize = 0;
#if defined(LINUX) || defined(PS3)
	nAttrib = _A_SUBDIR;
#else
	nAttrib = _A_SUBDIR | _A_RDONLY;
#endif
	tAccess = -1;
	tCreate = -1;
	tWrite  = -1;
}

//! Puts the memory statistics into the given sizer object
//! According to the specifications in interface ICrySizer
void CCryPak::GetMemoryStatistics(ICrySizer *pSizer)
{
	{
#if	!defined(PS3_CRYSIZER_HEAP_TRAVERSAL)		
		AUTO_READLOCK(m_csZips);
#endif		
		SIZER_SUBCOMPONENT_NAME(pSizer, "Zips");
		pSizer->AddObject( m_arrZips );		
	}

	{
#if	!defined(PS3_CRYSIZER_HEAP_TRAVERSAL)		
		AUTO_READLOCK(m_csFindData);
#endif		
		SIZER_SUBCOMPONENT_NAME(pSizer, "FindData");
		pSizer->AddObject( m_setFindData );		
	}

	{
#if	!defined(PS3_CRYSIZER_HEAP_TRAVERSAL)		
		AUTO_READLOCK(m_csCachedFiles);
#endif		
		SIZER_SUBCOMPONENT_NAME(pSizer, "CachedFiles");
		pSizer->AddObject( m_setCachedFiles );		
	}

	{
#if	!defined(PS3_CRYSIZER_HEAP_TRAVERSAL)		
		AUTO_READLOCK(m_csOpenFiles);
#endif		
		SIZER_SUBCOMPONENT_NAME(pSizer, "OpenFiles");
		pSizer->AddObject( m_arrOpenFiles );		
	}

	{
		SIZER_SUBCOMPONENT_NAME(pSizer, "Resource Lists");

		if (m_pEngineStartupResourceList)
			m_pEngineStartupResourceList->GetMemoryStatistics(pSizer);

		if (m_pLevelResourceList)
			m_pLevelResourceList->GetMemoryStatistics(pSizer);

		if (m_pNextLevelResourceList)
			m_pNextLevelResourceList->GetMemoryStatistics(pSizer);
	}
}

size_t CCryPakFindData::sizeofThis()const
{
	size_t nSize = sizeof(*this);
	for (FileMap::const_iterator it = m_mapFiles.begin(); it != m_mapFiles.end(); ++it)
	{
		nSize += sizeof(FileMap::value_type);
		nSize += it->first.capacity();
	}
	return nSize;
}

void CCryPakFindData::GetMemoryUsage( ICrySizer *pSizer ) const
{
	pSizer->AddObject( m_mapFiles );
}

bool CCryPak::MakeDir(const char* szPathIn,bool bGamePathMapping)
{
	stack_string pathStr = szPathIn;
	// Determine if there is a period ('.') after the last slash to determine if the path contains a file.
	// This used to be a strchr on the whole path which could contain a period in a path, such as network domain paths (domain.user).
	bool bPathContainsFile = false;
	size_t findDotFromPos = pathStr.rfind(g_cNativeSlash);
	if(findDotFromPos == stack_string::npos)
	{
		findDotFromPos = pathStr.rfind(g_cNonNativeSlash);
		if(findDotFromPos == stack_string::npos)
			findDotFromPos = 0;
	}
	size_t dotPos = pathStr.find('.', findDotFromPos);
	if (dotPos != stack_string::npos)
	{
		pathStr = PathUtil::GetPath( stack_string(szPathIn) );
	}
	else
	{
		pathStr = szPathIn;
	}

	const char *szPath = pathStr;
	if (0 == szPath[0])
	{
		return true;
	}

#if !defined(PS3)

	char tempPath[MAX_PATH];
	int nFlagsAdd = (!bGamePathMapping)?FLAGS_PATH_REAL:0;
	szPath = AdjustFileName(szPath,tempPath,FLAGS_FOR_WRITING|nFlagsAdd );

	char newPath[MAX_PATH];
	char *q = newPath;

	memset(newPath,0,sizeof(newPath));

	const char* p = szPath;
	bool bUNCPath = false;
	// Check for UNC path.
	if (szPath[0] == g_cNativeSlash && szPath[1] == g_cNativeSlash)
	{
		// UNC path given, Skip first 2 slashes.
		*q++ = *p++;
		*q++ = *p++;
	}

	for (; *p; )
	{
		while (*p != g_cNonNativeSlash && *p != g_cNativeSlash && *p)
		{
			*q++ = *p++;
		}
		CryCreateDirectory( newPath,NULL );
		if (*p)
		{
			if (*p != g_cNonNativeSlash)
			{
				*q++ = *p++;
			}
			else
			{
				*q++ = g_cNativeSlash;
				p++;
			}
		}
	}
	int res = CryCreateDirectory( szPath,NULL );




























#endif

	if (res != 0)
	{
		// [K01]:CryCreateDirectory() sets an error if the path already exists, which is probably wrong behaviour.
		// CryGetFileAttributes() and WinBase.cpp's GetFileAttributes() both have incorrect behaviour.
		// None of these are worth the risk of fixing, in case when there is code relying on the existing behaviour.
#ifdef LINUX
		struct stat statbuf;
		if ((stat(szPath, &statbuf) == -1) || (!S_ISDIR(statbuf.st_mode)))
			return false; // couldn't create such directory
#else
		if (GetLastError() == ERROR_PATH_NOT_FOUND)
			return false; // couldn't create such directory
#endif
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////
// open the physical archive file - creates if it doesn't exist
// returns NULL if it's invalid or can't open the file
ICryArchive* CCryPak::OpenArchive (
	const char* szPath, unsigned int nFlags, IMemoryBlock* pData)
{
	LOADING_TIME_PROFILE_SECTION
	PROFILE_DISK_OPEN;
	MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_Other, 0, "CryPak (%s)", szPath);

	char szFullPathBuf[CCryPak::g_nMaxPath];

	const char* szFullPath = AdjustFileName(szPath, szFullPathBuf, FLAGS_PATH_REAL );

	// if it's simple and read-only, it's assumed it's read-only
	if (nFlags & ICryArchive::FLAGS_OPTIMIZED_READ_ONLY)
		nFlags |= ICryArchive::FLAGS_READ_ONLY;

	unsigned nFactoryFlags = 0;

	if (nFlags & ICryArchive::FLAGS_IN_MEMORY)
		nFactoryFlags |= ZipDir::CacheFactory::FLAGS_IN_MEMORY;

	if (nFlags & ICryArchive::FLAGS_DONT_COMPACT)
		nFactoryFlags |= ZipDir::CacheFactory::FLAGS_DONT_COMPACT;

	if (nFlags & ICryArchive::FLAGS_READ_ONLY)
		nFactoryFlags |= ZipDir::CacheFactory::FLAGS_READ_ONLY;

	if (nFlags & ICryArchive::FLAGS_CREATE_NEW)
		nFactoryFlags |= ZipDir::CacheFactory::FLAGS_CREATE_NEW;

	if (nFlags & ICryArchive::FLAGS_FILENAMES_AS_CRC32)
		nFactoryFlags |= ZipDir::CacheFactory::FLAGS_FILENAMES_AS_CRC32;

	ICryArchive* pArchive = FindArchive(szFullPath);
	if (pArchive)
	{
		// check for compatibility
		if (!(nFlags & ICryArchive::FLAGS_RELATIVE_PATHS_ONLY) && (pArchive->GetFlags() & ICryArchive::FLAGS_RELATIVE_PATHS_ONLY))
			pArchive->ResetFlags(ICryArchive::FLAGS_RELATIVE_PATHS_ONLY);

		// we found one
		if (nFlags & ICryArchive::FLAGS_OPTIMIZED_READ_ONLY)
		{
			if (pArchive->GetClassId() == CryArchive::gClassId)
				return pArchive; // we can return an optimized archive

			//if (!(pArchive->GetFlags() & ICryArchive::FLAGS_READ_ONLY))
			return NULL; // we can't let it open read-only optimized while it's open for RW access
		}
		else
		{
			if (!(nFlags & ICryArchive::FLAGS_READ_ONLY) && (pArchive->GetFlags() & ICryArchive::FLAGS_READ_ONLY))
			{
				// we don't support upgrading from ReadOnly to ReadWrite
				return NULL;
			}

			return pArchive;
		}

		return NULL;
	}

	string strBindRoot;

	//if (!(nFlags & ICryArchive::FLAGS_RELATIVE_PATHS_ONLY))
	strBindRoot = PathUtil::GetParentDirectory(szFullPath);

	// Check if file on disk exist.
	bool bFileExists = false;

	// if valid data is given and read only, then don't check if file excists
	if (pData && nFlags & ICryArchive::FLAGS_OPTIMIZED_READ_ONLY)
	{
		bFileExists = true;
	} else {
#if defined(LINUX)
		if (access(szFullPath, R_OK) == 0)
			bFileExists = true;

















#else
		FILE *fp = CIOWrapper::Fopen(szFullPath, "rb");
		if (fp)
		{
			CIOWrapper::Fclose(fp);
			bFileExists = true;
		}
#endif
	}

	if (!bFileExists && (nFactoryFlags & ZipDir::CacheFactory::FLAGS_READ_ONLY))
	{
		// Pak file not found.
		CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"Cannot open Pak file %s",szFullPath );
		return NULL;
	}

	ZipDir::CacheFactory factory (g_pPakHeap, ZipDir::ZD_INIT_FAST, nFactoryFlags);
	if (nFlags & ICryArchive::FLAGS_OPTIMIZED_READ_ONLY)
	{
		ZipDir::CachePtr cache = 0;
		if (pData)
			cache = factory.New(szFullPath, pData);
		else
			cache = factory.New(szFullPath, (nFactoryFlags & ZipDir::CacheFactory::FLAGS_IN_MEMORY) != 0);

		if (cache)
			return new CryArchive (this, strBindRoot, cache, nFlags);
	}
	else
	{
#ifndef OPTIMIZED_READONLY_ZIP_ENTRY
		ZipDir::CacheRWPtr cache = factory.NewRW(szFullPath);
		if (cache)
			return new CryArchiveRW(this, strBindRoot, cache, nFlags);
#else
		return 0;
#endif
	}

	return 0;
}

uint32 CCryPak::ComputeCRC( const char* szPath, uint32 nFileOpenFlags )
{
	assert(szPath);

	uint32 dwCRC=0;

	// generate crc32
	{
		const uint32 dwChunkSize=1024*1024;		// 1MB chunks
		uint8 *pMem = new uint8[dwChunkSize];

		if(!pMem)
			return ZipDir::ZD_ERROR_NO_MEMORY;

		uint32 dwSize = 0;
		PROFILE_DISK_OPEN;

		{
			FILE *fp = 	gEnv->pCryPak->FOpen(szPath, "rb", nFileOpenFlags);

			if(fp)
			{
				gEnv->pCryPak->FSeek(fp, 0, SEEK_END);
				dwSize = gEnv->pCryPak->FGetSize(fp);
				gEnv->pCryPak->FClose(fp);
			}
		}

		// rbx open flags, x is a hint to not cache whole file in memory.
		FILE *fp = 	gEnv->pCryPak->FOpen(szPath, "rbx", nFileOpenFlags);

		if(!fp)
		{
			delete []pMem;
			return ZipDir::ZD_ERROR_INVALID_PATH;
		}

		// load whole file in chunks and compute CRC
		while(dwSize>0)
		{
			uint32 dwLocalSize = min(dwSize,dwChunkSize);

			INT_PTR read = gEnv->pCryPak->FReadRaw(pMem,1,dwLocalSize,fp);		

			assert(read==dwLocalSize);

			dwCRC = crc32(dwCRC,pMem,dwLocalSize);
			dwSize-=dwLocalSize;
		}

		delete []pMem;

		gEnv->pCryPak->FClose(fp);
	}

	return dwCRC;
}

bool CCryPak::ComputeMD5(const char* szPath, unsigned char* md5)
{
	if(!szPath || !md5)
		return false;

	MD5Context context;
	MD5Init(&context);

	// generate checksum
	{
		const uint32 dwChunkSize=1024*1024;		// 1MB chunks
		uint8 *pMem = new uint8[dwChunkSize];

		if(!pMem)
			return false;

		uint32 dwSize = 0;

		PROFILE_DISK_OPEN;
		{
			FILE *fp = 	gEnv->pCryPak->FOpen(szPath, "rb");

			if(fp)
			{
				dwSize = gEnv->pCryPak->FGetSize(fp);
				gEnv->pCryPak->FClose(fp);
			}
		}

		// rbx open flags, x is a hint to not cache whole file in memory.
		FILE *fp = 	gEnv->pCryPak->FOpen(szPath, "rbx");

		if(!fp)
		{
			delete []pMem;
			return false;
		}

		// load whole file in chunks and compute Md5
		while(dwSize>0)
		{
			uint32 dwLocalSize = min(dwSize,dwChunkSize);

			INT_PTR read = gEnv->pCryPak->FReadRaw(pMem,1,dwLocalSize,fp);		
			assert(read==dwLocalSize);

			MD5Update(&context, pMem, dwLocalSize);
			dwSize-=dwLocalSize;
		}

		delete []pMem;

		gEnv->pCryPak->FClose(fp);
	}

	MD5Final(md5, &context);
	return true;
}


int CCryPak::ComputeCachedPakCDR_CRC(const char* filename, bool useCryFile /*=true*/ ) 
{
	uint32 offset = 0;
	uint32 size = 0;
	int retval = 0;

	//get Central Directory Record position and length
	gEnv->pCryPak->GetCachedPakCDROffsetSize(filename, offset, size);

	CCryFile file;
	FILE *fp;

	if( useCryFile )
	{		
		file.Open(filename, "rb", ICryPak::FOPEN_ONDISK);
		file.Seek( offset, 0 );

		CryLog( "CRC: Used CryFile method, fp is 0x%p", file.GetHandle() );
	}
	else
	{
		fp = CIOWrapper::Fopen(filename, "rb");
		CIOWrapper::Fseek(fp, offset, 0);

		CryLog( "CRC: Used CIO method, fp is 0x%p", fp );
	}

	const uint32 dwChunkSize=1024*1024;		// 1MB chunks
	uint8 *pMem = new uint8[dwChunkSize];

	if(!pMem)
		return ZipDir::ZD_ERROR_NO_MEMORY;

	uint32 dwSize = size;
	uint32 dwCRC=0;

	// load Central Directory Record in chunks and compute CRC
	while(dwSize>0)
	{
		uint32 dwLocalSize = min(dwSize,dwChunkSize);

		size_t read = 0;
		if( useCryFile )
		{
			read = file.ReadRaw(pMem, dwLocalSize);
		}
		else
		{
			read = CIOWrapper::Fread(pMem,1,dwLocalSize,fp);		
		}

		assert(read==dwLocalSize);

		dwCRC = crc32(dwCRC,pMem,dwLocalSize);
		dwSize-=dwLocalSize;
	}


	delete []pMem;

	if( useCryFile )
	{
		file.Close();
	}
	else
	{
		CIOWrapper::Fclose(fp);
	}

	return dwCRC;
}

void CCryPak::Register (ICryArchive* pArchive)
{
	AUTO_MODIFYLOCK(m_csZips);
	ArchiveArray::iterator it = std::lower_bound (m_arrArchives.begin(), m_arrArchives.end(), pArchive, CryArchiveSortByName());
	m_arrArchives.insert (it, pArchive);
}

void CCryPak::Unregister (ICryArchive* pArchive)
{
	AUTO_MODIFYLOCK(m_csZips);
	if (pArchive)
		CryComment( "Closing PAK file: %s",pArchive->GetFullPath() );
	ArchiveArray::iterator it;
	if (m_arrArchives.size() < 16)
	{
		// for small array sizes, we'll use linear search
		it = std::find (m_arrArchives.begin(), m_arrArchives.end(), pArchive);
	}
	else
		it = std::lower_bound (m_arrArchives.begin(), m_arrArchives.end(), pArchive, CryArchiveSortByName());

	if (it != m_arrArchives.end() && *it == pArchive)
		m_arrArchives.erase (it);
	else
		assert (0); // unregistration of unregistered archive
}

ICryArchive* CCryPak::FindArchive (const char* szFullPath)
{
	AUTO_READLOCK(m_csZips);
	ArchiveArray::iterator it = std::lower_bound (m_arrArchives.begin(), m_arrArchives.end(), szFullPath, CryArchiveSortByName());
	if (it != m_arrArchives.end() && !stricmp(szFullPath, (*it)->GetFullPath()))
		return *it;
	else
		return NULL;
}

const ICryArchive* CCryPak::FindArchive (const char* szFullPath) const
{
	AUTO_READLOCK(m_csZips);
	ArchiveArray::const_iterator it = std::lower_bound (m_arrArchives.begin(), m_arrArchives.end(), szFullPath, CryArchiveSortByName());
	if (it != m_arrArchives.end() && !stricmp(szFullPath, (*it)->GetFullPath()))
		return *it;
	else
		return NULL;
}


// compresses the raw data into raw data. The buffer for compressed data itself with the heap passed. Uses method 8 (deflate)
// returns one of the Z_* errors (Z_OK upon success)
// MT-safe
int CCryPak::RawCompress (const void* pUncompressed, unsigned long* pDestSize, void* pCompressed, unsigned long nSrcSize, int nLevel)
{
	return ZipDir::ZipRawCompress(g_pPakHeap, pUncompressed, pDestSize, pCompressed, nSrcSize, nLevel);
}

// Uncompresses raw (without wrapping) data that is compressed with method 8 (deflated) in the Zip file
// returns one of the Z_* errors (Z_OK upon success)
// This function just mimics the standard uncompress (with modification taken from unzReadCurrentFile)
// with 2 differences: there are no 16-bit checks, and
// it initializes the inflation to start without waiting for compression method byte, as this is the
// way it's stored into zip file
int CCryPak::RawUncompress (void* pUncompressed, unsigned long* pDestSize, const void* pCompressed, unsigned long nSrcSize)
{
	return ZipDir::ZipRawUncompress(g_pPakHeap, pUncompressed, pDestSize, pCompressed, nSrcSize);
}

//Adds a file to the whitelist of available files along with the CRC of the pak header.
void CCryPak::AddPakToWhiteList( const uint32 filenameCRC, const uint32 headerCRC )
{
	m_whiteList.insert(TWhiteListPair(filenameCRC, headerCRC));
}

//Tells CryPak that the whitelist is complete and ready for use. Any pak files outside of the whitelist will not be opened.
//This may also automatically close any files that are open but not in the whitelist.
void CCryPak::EnableWhiteList( const bool bEnable )
{
	m_bUseWhiteList = bEnable;

	if(bEnable)
	{
		//Iterate through the open paks and close any that aren't in the list
		for (ZipArray::iterator it = m_arrZips.begin(); it != m_arrZips.end(); ++it)
		{
			const char *szFullPath = it->GetFullPath();
			const char *szFullPathNoDrive = strchr( szFullPath, ':' );
			szFullPathNoDrive = ( szFullPathNoDrive == NULL ? szFullPath : szFullPathNoDrive+1 );
			uint32 fullPathCRC = crc32(0,(const Bytef *)szFullPathNoDrive,strlen(szFullPathNoDrive));
			//Test for the presence of the filename in the map that defines the whitelist
			if( m_whiteList.find(fullPathCRC) == m_whiteList.end() )
			{
				//Hackzilla strikes again.
				CryWarning( VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Pakfile %s was previously opened but is not present in the whitelist. This file will be closed.", szFullPath );
				//Invoke the global handler in PlatformOS, close the pak and return false.
				IPlatformOS *pPlatformOS = gEnv->pSystem->GetPlatformOS();
				if( pPlatformOS != NULL )
				{
					pPlatformOS->HandleArchiveVerificationFailure( );
				}
				else
				{
					//Notify the POS about the detected corruption so it can handle it
					CryWarning( VALIDATOR_MODULE_SYSTEM, VALIDATOR_COMMENT, "Issue detected before PlatformOS has been initialized. Setting a flag so that it can respond when ready." );
					gEnv->pSystem->AddPlatformOSCreateFlag( ( uint8 )IPlatformOS::eCF_EarlyCorruptionDetected );
				}
				ClosePack(szFullPath);
				it = m_arrZips.begin();
			}
		}
	}
}

//Tells CryPak that the CRCs of headers of paks in the whitelist should be checked against the stored CRCs to verify their authenticity.
//This will be applied retroactively to paks already open as well as new paks that are opened.
void CCryPak::EnableWhiteListCDRCRCChecking( const bool bEnable )
{
	m_bUseWhiteListCRCs = bEnable;

	if(bEnable)
	{
		//Iterate through the open paks and search for CRC mismatches
		for (ZipArray::iterator it = m_arrZips.begin(); it != m_arrZips.end(); ++it)
		{
			const char *szFullPath = it->GetFullPath();
			const char *szFullPathNoDrive = strchr( szFullPath, ':' );
			szFullPathNoDrive = ( szFullPathNoDrive == NULL ? szFullPath : szFullPathNoDrive+1 );
			uint32 fullPathCRC = crc32(0,(const Bytef *)szFullPathNoDrive,strlen(szFullPathNoDrive));
			TWhiteListMap::const_iterator whiteListIt = m_whiteList.find(fullPathCRC);
			//Compute the archive CRC32
			uint32 computedCRC = ComputeCachedPakCDR_CRC(szFullPath, false);
			if( whiteListIt->second != computedCRC )
			{
				//Son of Hackzilla
				CryWarning( VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Pakfile %s has a different header CRC to the stored version in the whitelist. %8X != %8X", szFullPath, computedCRC, whiteListIt->second );
				//Invoke the global handler in PlatformOS, close the pak and return false.
				IPlatformOS *pPlatformOS = gEnv->pSystem->GetPlatformOS();
				if( pPlatformOS != NULL )
				{
					pPlatformOS->HandleArchiveVerificationFailure( );
				}
				else
				{
					//Notify the POS about the detected corruption so it can handle it
					CryWarning( VALIDATOR_MODULE_SYSTEM, VALIDATOR_COMMENT, "Issue detected before PlatformOS has been initialized. Setting a flag so that it can respond when ready." );
					gEnv->pSystem->AddPlatformOSCreateFlag( ( uint8 )IPlatformOS::eCF_EarlyCorruptionDetected );
				}
				ClosePack(szFullPath);
			}
		}
	}
}

// returns the current game directory, with trailing slash (or empty string if it's right in MasterCD)
// this is used to support Resource Compiler which doesn't have access to this interface:
// in case all the contents is located in a subdirectory of MasterCD, this string is the subdirectory name with slash
/*const char* CCryPak::GetGameDir()
{
return m_strPrepend.c_str();
}
*/

//////////////////////////////////////////////////////////////////////////
void CCryPak::RecordFileOpen( const ERecordFileOpenList eList )
{
	m_eRecordFileOpenList = eList;

	//CryLog("RecordFileOpen(%d)",(int)eList);

	switch(m_eRecordFileOpenList)
	{
	case RFOM_Disabled:
	case RFOM_EngineStartup:
	case RFOM_Level:
		break;

	case RFOM_NextLevel:
	default:
		assert(0);
	}
}

//////////////////////////////////////////////////////////////////////////
ICryPak::ERecordFileOpenList CCryPak::GetRecordFileOpenList()
{
	return m_eRecordFileOpenList;
}

//////////////////////////////////////////////////////////////////////////
IResourceList* CCryPak::GetResourceList( const ERecordFileOpenList eList )
{
	switch(eList)
	{
	case RFOM_EngineStartup: 
		return m_pEngineStartupResourceList;
	case RFOM_Level: 
		return m_pLevelResourceList;
	case RFOM_NextLevel: 
		return m_pNextLevelResourceList;
	case RFOM_Disabled:
	default:
		assert(0);
	}
	return 0; 
}

//////////////////////////////////////////////////////////////////////////
void CCryPak::SetResourceList( const ERecordFileOpenList eList,IResourceList* pResourceList )
{
	switch(eList)
	{
	case RFOM_EngineStartup: 
		m_pEngineStartupResourceList = pResourceList;
		break;
	case RFOM_Level:
		m_pLevelResourceList = pResourceList;
		break;
	case RFOM_NextLevel: 
		m_pNextLevelResourceList = pResourceList;
		break;

	case RFOM_Disabled:
	default:
		assert(0);
	}
}

//////////////////////////////////////////////////////////////////////////
void CCryPak::RecordFile( FILE *in, const char *szFilename )
{
	//if (m_pLog)
	//		CryComment( "File open: %s",szFilename );

	if (m_eRecordFileOpenList!=ICryPak::RFOM_Disabled)
	{
		if(strnicmp("%USER%",szFilename,6)!=0)		// ignore path to OS settings
		{
			IResourceList *pList = GetResourceList(m_eRecordFileOpenList);

			if(pList)
				pList->Add( szFilename );
		}
	}

	std::vector<ICryPakFileAcesssSink *>::iterator it, end=m_FileAccessSinks.end();

	for(it=m_FileAccessSinks.begin();it!=end;++it)
	{
		ICryPakFileAcesssSink *pSink = *it;

		pSink->ReportFileOpen(in,szFilename);
	}
}


void CCryPak::OnMissingFile (const char* szPath)
{






	AUTO_LOCK_CS(m_csMissingFiles);
	if (m_pPakVars->nLogMissingFiles)
	{
		std::pair<MissingFileMap::iterator, bool> insertion = m_mapMissingFiles.insert (MissingFileMap::value_type(szPath,1));
		if (m_pPakVars->nLogMissingFiles >= 2 && (insertion.second || m_pPakVars->nLogMissingFiles >= 3))
		{
			// insertion occured
			char szFileName[64];
			sprintf_s(szFileName, "MissingFiles%d.log", m_pPakVars->nLogMissingFiles);
			FILE* f = CIOWrapper::Fopen(szFileName, "at");
			if (f)
			{
				fprintf (f, "%s\n", szPath);
				CIOWrapper::Fclose(f);
			}
		}
		else
			++insertion.first->second;  // increase the count of missing files
	}

}

bool CCryPak::DisableRuntimeFileAccess( bool status, uint32 threadId )
{
	bool prev = false;
	if (threadId == m_mainThreadId)
	{
		prev = m_disableRuntimeFileAccess[0];
		m_disableRuntimeFileAccess[0] = status;
	}
	else if (threadId == m_renderThreadId)
	{
		prev = m_disableRuntimeFileAccess[1];
		m_disableRuntimeFileAccess[1] = status;
	}
	return prev;
}

static char* cry_strdup(const char* szSource)
{
	size_t len = strlen(szSource);
	char* szResult = (char*)malloc(len+1);
	memcpy (szResult, szSource, len+1);
	return szResult;
}


ICryPak::PakInfo* CCryPak::GetPakInfo()
{
	AUTO_READLOCK(m_csZips);
	PakInfo* pResult = (PakInfo*)malloc (sizeof(PakInfo) + sizeof(PakInfo::Pak)*m_arrZips.size());
	assert(pResult);
	pResult->numOpenPaks = m_arrZips.size();
	for (unsigned i = 0; i < m_arrZips.size(); ++i)
	{
		pResult->arrPaks[i].szBindRoot = cry_strdup(m_arrZips[i].strBindRoot.c_str());
		pResult->arrPaks[i].szFilePath = cry_strdup(m_arrZips[i].GetFullPath());
		pResult->arrPaks[i].nUsedMem = m_arrZips[i].sizeofThis();
	}
	return pResult;
}

//////////////////////////////////////////////////////////////////////////
void CCryPak::FreePakInfo (PakInfo* pPakInfo)
{
	for (unsigned i = 0; i < pPakInfo->numOpenPaks; ++i)
	{
		free((void*)pPakInfo->arrPaks[i].szBindRoot);
		free((void*)pPakInfo->arrPaks[i].szFilePath);
	}
	free(pPakInfo);
}

//////////////////////////////////////////////////////////////////////////
void CCryPak::RegisterFileAccessSink( ICryPakFileAcesssSink *pSink )
{
	assert(pSink);

	if(std::find(m_FileAccessSinks.begin(),m_FileAccessSinks.end(),pSink)!=m_FileAccessSinks.end())
	{
		// was already registered
		assert(0);
		return;
	}

	m_FileAccessSinks.push_back(pSink);
}


//////////////////////////////////////////////////////////////////////////
void CCryPak::UnregisterFileAccessSink( ICryPakFileAcesssSink *pSink )
{
	assert(pSink);

	std::vector<ICryPakFileAcesssSink *>::iterator it = std::find(m_FileAccessSinks.begin(),m_FileAccessSinks.end(),pSink);

	if(it==m_FileAccessSinks.end())
	{
		// was not there
		assert(0);
		return;
	}

	m_FileAccessSinks.erase(it);
}

//////////////////////////////////////////////////////////////////////////
bool CCryPak::RemoveFile(const char* pName)
{
	char szFullPathBuf[g_nMaxPath];
	const char *szFullPath = AdjustFileName(pName, szFullPathBuf, 0);
	uint32 dwAttr = CryGetFileAttributes(szFullPath);
	bool ok = false;
	if (dwAttr != INVALID_FILE_ATTRIBUTES && dwAttr != FILE_ATTRIBUTE_DIRECTORY)
	{
#ifdef WIN32
		SetFileAttributes( szFullPath,FILE_ATTRIBUTE_NORMAL );
#endif
		ok = (remove(szFullPath) == 0);
	}
	return ok;
}

//////////////////////////////////////////////////////////////////////////
static void Deltree( const char *szFolder, bool bRecurse )
{
	__finddata64_t fd;
	string filespec = szFolder;
	filespec += "*.*";

	intptr_t hfil = 0;
	if ((hfil = _findfirst64(filespec.c_str(), &fd)) == -1)
	{
		return;
	}

	do
	{
		if (fd.attrib & _A_SUBDIR)
		{
			string name = fd.name;

			if ((name != ".") && (name != ".."))
			{
				if (bRecurse)
				{
					name = szFolder;
					name += fd.name;
					name += "/";

					Deltree(name.c_str(), bRecurse);
				}
			}
		}
		else
		{
			string name = szFolder;

			name += fd.name;

			DeleteFile(name.c_str());
		}

	} while(!_findnext64(hfil, &fd));

	_findclose(hfil);

	RemoveDirectory(szFolder);
}

//////////////////////////////////////////////////////////////////////////
bool CCryPak::RemoveDir(const char* pName, bool bRecurse)
{
	char szFullPathBuf[g_nMaxPath];
	const char *szFullPath = AdjustFileName(pName, szFullPathBuf, 0);

	bool ok = false;
	uint32 dwAttr = CryGetFileAttributes(szFullPath);
	if (dwAttr == FILE_ATTRIBUTE_DIRECTORY)
	{
		Deltree(szFullPath, bRecurse);
		ok = true;
	}
	return ok;
}

ILINE bool IsDirSep(const char c)
{
	return (c == CCryPak::g_cNativeSlash || c == CCryPak::g_cNonNativeSlash);
}

//////////////////////////////////////////////////////////////////////////
bool CCryPak::IsAbsPath(const char* pPath)
{
	return (pPath && ((pPath[0] && pPath[1] == ':' && IsDirSep(pPath[2]))
		|| IsDirSep(pPath[0])
		)
		);
}

//////////////////////////////////////////////////////////////////////////
void CCryPak::SetLog( IMiniLog *pLog )
{
	m_pLog = pLog;


	std::vector<string>::iterator stringVecIt;
	for (stringVecIt = m_arrMods.begin(); stringVecIt != m_arrMods.end(); ++stringVecIt)
	{
		string &sMOD = *stringVecIt;
		m_pLog->Log("Added MOD directory <%s> to CryPak", sMOD.c_str());
	}
}

void * CCryPak::PoolMalloc(size_t size)
{
	return g_pPakHeap->TempAlloc(size, "CCryPak::GetPoolRealloc");
}

void CCryPak::PoolFree(void * p)
{
	g_pPakHeap->FreeTemporary(p);
}

void CCryPak::Lock()
{
	m_csMain.LockModify();
}

void CCryPak::Unlock()
{
	m_csMain.UnlockModify();
}

void CCryPak::LockReadIO(bool bValue)
{
	CIOWrapper::m_bLockReads = bValue;
}

// gets the current pak priority
int CCryPak::GetPakPriority()
{
	return m_pPakVars->nPriority;
}

//////////////////////////////////////////////////////////////////////////

void CCryPak::TouchDummyFile(const char* acFilename)
{
	if (m_pPakVars->nTouchDummyFiles == 0)
		return;

	string sFilename = string("d:\\dummyfiles\\") + string(acFilename);

	FileAccessLogger::AddLabel(acFilename);

	if(FILE * f = CIOWrapper::Fopen(sFilename,"rb"))
	{
		char acBuffer[256];
		// offset into file to be sure this one is only touched
		fseek(f, 20 * 1024, SEEK_SET);
		CIOWrapper::Fread(acBuffer, 1, 256, f);
		CIOWrapper::Fclose(f);
	}
}

//////////////////////////////////////////////////////////////////////////
class CFilePoolMemoryBlock : public IMemoryBlock
{
public:
	virtual void * GetData() { return m_pData; }
	virtual int GetSize() { return m_nSize; }

	virtual ~CFilePoolMemoryBlock()
	{
		if (m_pData)
			g_pPakHeap->FreeTemporary(m_pData);
	}

	CFilePoolMemoryBlock( size_t nSize,const char *sUsage )
	{
		m_sUsage = sUsage;
		m_pData=g_pPakHeap->TempAlloc(nSize,m_sUsage.c_str());
		m_nSize=nSize;
	}

private:
	string m_sUsage;
	void* m_pData;
	size_t m_nSize;
};

IMemoryBlock* CCryPak::PoolAllocMemoryBlock( size_t nSize,const char *sUsage )
{
	return new CFilePoolMemoryBlock(nSize,sUsage);
}

//////////////////////////////////////////////////////////////////////////
void CCryPak::OnSystemEvent( ESystemEvent event,UINT_PTR wparam,UINT_PTR lparam )
{
	switch (event)
	{
	case ESYSTEM_EVENT_LEVEL_LOAD_START:
		m_fFileAcessTime = 0;

		CIOWrapper::AddLabel( "Level Load Start" );
		break;
	case ESYSTEM_EVENT_LEVEL_LOAD_END:
		{
			// Log used time.
			CryLog( "File access time during level loading: %.2f seconds",m_fFileAcessTime );
			m_fFileAcessTime = 0;
			CIOWrapper::AddLabel( "Level Load End" );
		}
		break;

	case ESYSTEM_EVENT_GAME_POST_INIT:
		CIOWrapper::AddLabel( "Game Post Init" );
		break;
	
	case ESYSTEM_EVENT_LEVEL_LOAD_PREPARE:
		CIOWrapper::AddLabel( "Level Load Prepare" );
#ifndef PS3
		CIOWrapper::m_bLockReads = true;
#endif
		break;

	case ESYSTEM_EVENT_LEVEL_PRECACHE_START:
		CIOWrapper::AddLabel( "Precache Start" );
		break;

	case ESYSTEM_EVENT_LEVEL_PRECACHE_END:
		CIOWrapper::AddLabel( "Precache End" );
#ifndef PS3
		CIOWrapper::m_bLockReads = false;
#endif
		break;
	}

}

// return offset in pak file (ideally has to return offset on DVD)
uint64 CCryPak::GetFileOffsetOnMedia(const char* sFilename)
{
  char szFullPathBuf[g_nMaxPath];
  const char *szFullPath = AdjustFileName(sFilename, szFullPathBuf, FOPEN_HINT_QUIET);

  assert(szFullPath);
  if (!szFullPath)
    return 0;

  ZipDir::Cache* pZip = 0;
	unsigned int nArchiveFlags;
  ZipDir::FileEntry* pFileEntry = FindPakFileEntry( szFullPath, nArchiveFlags, &pZip, false );
  if (!pFileEntry)
	{
		return 0;
	}
  return pZip->GetPakFileOffsetOnMedia() + (uint64)pFileEntry->nFileDataOffset;
}

//////////////////////////////////////////////////////////////////////////
ICustomMemoryHeap* CCryPak::GetInMemoryPakHeap()
{
	if (!m_pInMemoryPaksHeap)
	{
		m_pInMemoryPaksHeap = new CCustomMemoryHeap(true);
	}
	return m_pInMemoryPaksHeap;
}

//////////////////////////////////////////////////////////////////////////
CFileReadAheadCache* CCryPak::GetReadAheadCache()
{
	return m_pReadAheadCache;
}


bool CCryPak::SetPacksAccessible( bool bAccessible, const char* pWildcard, unsigned nFlags  )
{
	__finddata64_t fd;
	char cWorkBuf[g_nMaxPath];
	const char* cWork = AdjustFileName(pWildcard, cWorkBuf, nFlags);
	intptr_t h = _findfirst64 (cWork, &fd);
	string strDir = PathUtil::GetParentDirectory(pWildcard);
	if (h != -1)
	{
		do {
			SetPackAccessible( bAccessible, (strDir + "\\" + fd.name).c_str(), nFlags);
		} while(0 == _findnext64 (h, &fd));
		_findclose (h);
		return true;
	}
	return false;
}

bool CCryPak::SetPackAccessible( bool bAccessible, const char* pName, unsigned nFlags  )
{
	char szZipPathBuf[g_nMaxPath];
	const char* szZipPath = AdjustFileName(pName, szZipPathBuf, nFlags);

	AUTO_MODIFYLOCK(m_csZips);
	for (ZipArray::iterator it = m_arrZips.begin(); it != m_arrZips.end(); ++it)
	{
		if (!stricmp(szZipPath, it->GetFullPath()))
		{
			return it->pArchive->SetPackAccessible(bAccessible);			
		}
	}

	return true;
}

#include UNIQUE_VIRTUAL_WRAPPER(ICryPak)
